分类 技术 下的文章

Linux cheat 命令是一个实用程序,可以用来搜索和显示你想要使用的命令的使用示例。

互联网上有很多关于 Linux 和开源的信息,但是当你想要深入工作,通常需要一份便捷的文档。早在 Linux 出现之前的 Unix 系统中,就有 man(“manual” 的缩写)和 info 命令了,二者都会显示命令、配置文件、系统调用等的官方项目文档。

关于 maninfo 页面是对知晓如何使用工具的用户的有用提醒,还是为初次使用的用户提供介绍存在争议。不管怎样,maninfo 页面介绍了工具以及如何使用该工具,很少涉及特定任务以及如何完成它们。正是出于这个原因,开发了 cheat 命令。

例如,设想你想不起来如何 解压 tar 压缩包文件man 页面会给你展示所有的选项,但需要你将这些信息转换为命令:

tar -A [OPTIONS] ARCHIVE ARCHIVE
tar -c [-f ARCHIVE] [OPTIONS] [FILE...]
tar -d [-f ARCHIVE] [OPTIONS] [FILE...]
tar -t [-f ARCHIVE] [OPTIONS] [MEMBER...]
tar -r [-f ARCHIVE] [OPTIONS] [FILE...]
tar -u [-f ARCHIVE] [OPTIONS] [FILE...]
tar -x [-f ARCHIVE] [OPTIONS] [MEMBER...]

这确实是一些用户需要的,但是也使一些用户感到困惑。相比之下,cheat 命令会罗列常用命令:

$ cheat tar

# To extract an uncompressed archive:
tar -xvf /path/to/foo.tar

# To extract a .tar in specified Directory:
tar -xvf /path/to/foo.tar -C /path/to/destination/

# To create an uncompressed archive:
tar -cvf /path/to/foo.tar /path/to/foo/

# To extract a .tgz or .tar.gz archive:
tar -xzvf /path/to/foo.tgz
tar -xzvf /path/to/foo.tar.gz
[...]

这真是雪中送炭!

Linux Cheat 命令

cheat 命令是一个实用程序,可以用来搜索和显示你想要使用的命令的使用示例。如大多数 Unix 命令一样,同一个概念有多种不同的实现方式,它包括一个 使用 Go 编写 的和一个由我帮助维护的 仅用 100 行 Bash 编写 的两个版本。

若要安装 Go 版本的,下载 最新版 并将它放在某个 路径 中,例如 ~/.local/bin//usr/local/bin 中。若安装 Bash 版本,下载最新版并运行 install-cheat.sh 脚本:

$ sh ./install-cheat.sh

如需配置后安装,请使用 自动工具(Autotools):

$ aclocal ; autoconf
$ automake --add-missing ; autoreconf
$ ./configure --prefix=$HOME/.local
$ make
$ make install

在 Linux 中安装 Cheat 程序

Cheat 只是包含常用命令的纯文本文件。该程序可以从 github.com/cheat/cheatsheets 获得。当你第一次运行命令时,Go 版本会自动为你下载支持列表。如果你使用 Bash 版本,用 --fetch 选项可以下载支持列表:

$ cheat --fetch

man 一样,你的系统上可以有多个备忘单集合。 Go 版本的 cheat 使用 YAML 配置文件来定义每个集合的位置。Bash 版本在安装过程中定义了路径,默认下载 github.com/cheat/cheatsheets 集合以及 opensource.com 自己的 gitlab.com/opensource.com/cheatsheets 集合。

列出 Cheat 支持项目

使用 --list 选项即可查看 cheat 支持的项目:

$ cheat --list
7z
ab
acl
alias
ansi
ansible
ansible-galaxy
ansible-vault
apk
[...]

使用 Cheat 查看 Linux 命令

使用 cheat 查看命令如同使用 maninfo 查看一样简单。只需要输入你需要查询的命令即可:

$ cheat alias

# To show a list of your current shell aliases:
alias

# To alias `ls -l` to `ll`:
alias ll='ls -l'

默认情况下,cheat 命令会使用你的 环境变量 PAGER 中指定的分页器。你可以在运行 cheat 命令前改写 PAGER 变量值,暂时修改环境变量。

$ PAGER=most cheat less

如果你只是想在没有 PAGER 的情况下将 cheat 输出 到终端里,在 Bash 版中有 --cat 选项可以使用:

$ cheat --cat less

这并不是作弊

cheat 系统抓住了要害,你不必拼凑有关如何使用命令的线索,你只需按照示例进行操作即可。当然,对于复杂的命令,它不是深入研究实际文档的捷径,但为了快速借用,它还是可以的。

甚至你可以通过将文件放入其中一个备忘单集合中,来创建自己的备忘单。好消息是,因为这些项目是开源的,所以你可以将你的个人备忘单贡献给 GitHub 集合。另一个好消息是,当有新的 opensource.com 备忘单 版本发布时,我们将从现在开始包含纯文本版本,以便你可以将其添加到你的收藏中。

该命令称为 “ 作弊 cheat ”,但正如任何 Linux 用户都会向你保证的那样,它实际上并不是作弊。它只是以开源的方式工作得更巧妙。


via: https://opensource.com/article/22/6/linux-cheat-command

作者:Seth Kenlon 选题:lkxed 译者:Donkey 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在 GNOME 桌面中创建自定义浅色和深色壁纸的简单指南。

GNOME 42 将期待已久的浅色和深色主题引入了 GNOME 桌面。它还带来了浅色和深色版壁纸,当你在浅色和深色主题之间切换时,它会自动改变。

因此,默认情况下,GNOME 会为你提供几组预配置的浅色和深色壁纸。

但是,如果你想要在主题更改时可以自动更改的别的壁纸怎么办?

以下是在 GNOME 中为浅色和深色主题配置和创建自定义壁纸的方法。

如何为 GNOME 创建自定义浅色和深色壁纸

首先,确保有两个版本的壁纸。通常,它们应该是标准的 PNG 或 JPG 图像。例如,我们在演示中使用了以下两个壁纸。

Sample light and dark wallpaper for demo

但是,如果你没有合适的浅色和深色壁纸,或正在寻找更多壁纸,在本指南的末尾,我会告诉你如何获取它们或准备你自己的。

跟着我来。

其次,我们需要为自己创建一个模式文件。壁纸的自动更换由名为 adwaita.xml 的 XML 文件处理,该文件定义了特定的浅色和深色背景标签。因此,我们将为壁纸创建 XML 文件。

为此,从 GitLab 复制 adwaita.xml 的内容并创建一个新的 XML 文件(链接在下面)。你应该在这个文件中看到两个标签:filenamefilename-dark。这两个 XML 标记包含两个壁纸的完全限定路径。在这两个标签下添加图片的路径,如下所示。

从这里下载 XML 文件 (adwaita.xml.in)

Change the XML file

第三步,使用你想要的任何名称将此文件保存到 /home/YOUR_NAME/.local/share/gnome-background-properties(请将 YOUR_NAME 替换为你的用户名)。如果 gnome-background-properties 不存在,请创建它们。对此示例,我使用了 my_cool_backgrounds.xml

Save the file

都准备好了。最后,打开设置并转到外观选项卡,你应该会看到选项中出现新的壁纸。

选择你的自定义浅色和深色壁纸并享受。

The appearance tab now has your custom light and dark wallpaper

如何下载或制作你的动态壁纸

你一定会想,“谁有时间去寻找和创建深浅版本的壁纸”?一些网站为你提供现成的动态壁纸,你可以轻松下载和安装。

我推荐的一个网站是 dynamicwallpaper.club,它为 macOS 提供了一些高达 6K 的优秀高质量壁纸。你可以轻松下载它们。

此外,如果你打算从上述网站下载,请记住该网站的图像是 heic 格式,因为该网站适用于 macOS。高效视频编码(HEIC)是 Apple 的 HEIF(高效图像文件格式)的专有版本。

你需要一个驱动来查看和转换 Ubuntu 或 Fedora Linux 中的动态 heic 图像。那么,如何将它们转换为适用于 Linux 系统呢?打开终端并运行以下命令来安装驱动。

Ubuntu 用户:

sudo apt install heif-gdk-pixbuf

Fedora 用户:

sudo dnf install libheif

仅适用于带有 KDE Plasma 的 Fedora/Ubuntu(没有此插件,Plasma 应用无法打开 heic 图像):

sudo apt install qt-heif-image-plugin
sudo dnf install qt-heif-image-plugin

最后,使用你喜欢的图像查看器打开 heic 图像并将其保存为 JPG/PNG。

Custom Light and Dark wallpaper in GNOME – transition

最后,别忘了在下面的评论部分告诉我你是否可以为 GNOME 创建自定义深色和浅色壁纸。

干杯。


via: https://www.debugpoint.com/custom-light-dark-wallpaper-gnome/

作者:Arindam 选题:lkxed 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用 LaTeX 标记语言来撰写文档。

LaTeX 文件准备系统有一段有趣的历史。在 1968 年,程序员 Don Knuth 用一种老式印刷排版方式,撰写了他的第一本书《 计算机程序设计艺术 The Art of Computer Programming 》。当他在 1976 年出版第二版时,出版商已经转向现代照相排版技术。

Knuth 对新版本的外观不满意。他从程序员的角度解决问题,决定创建他自己的文字处理系统,这样以后他出版的书就可以以相同格式排版,拥有相同的外观。因此,Don Knuth 在 1978 年编写了第一版 TeX 。

几年后,Leslie Lamport 创建了一组宏定义,以便作者更容易编写复杂文档。Lamport 的宏定义扩展,即 LaTeX,有效地扩展了 TeX 能够轻松创建各种文档。例如,许多学术组织使用 LaTeX 出版期刊和论文集。

使用 LaTeX 编写文档

通过写一些短文就可以很容易掌握 LaTeX 基础。让我们从 Opensource.com 介绍页面借用一下内容,创建一个示例:

$ cat about.tex 
\documentclass{article}
\begin{document}

Opensource.com is a premier, daily publication focused on
open source and Linux tutorials, stories, and resources.

We're a diverse and inviting group, made up of staff
editors, Correspondents, contributors, and readers. We
value differences in skills, talents, backgrounds, and
experiences. There are a few different ways to get involved
as a reader or a writer.

\end{document}

类似其他文档格式程序, LaTeX 会将单词汇集起来,填充成段落 。这意味着你可以在段落中间添加新文本,而不用担心最终文档的段落参差不齐。只要你不在段落中添加空行, LaTeX 就会创建完全对齐的段落。当它找到一个空行时, LaTeX 会开启一个新段落。

LaTeX 需要一些定义文档的控制语句。任何 LaTeX 文档应当以“文档类别”声明开始。LaTeX 支持多种文档,包括书信、书籍和文章。例如,我使用 \documentclass{article} 设置类别为 “文章” 。

使用 \begin{document}\end{document} 声明来定义文本的开始和结束。如果你在 \begin{document} 前添加了文本,那么 LaTeX 会报错。在 \end{document} 之后的文本都会被忽略。

使用 LaTeX 的 latex 命令处理文档:

$ latex about.tex
This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2021) (preloaded format=latex)
 restricted \write18 enabled.
entering extended mode
(./about.tex
LaTeX2e <2020-10-01> patch level 4
(/usr/share/texlive/texmf-dist/tex/latex/base/article.cls
Document Class: article 2020/04/10 v1.4m Standard LaTeX document class
(/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo))
(/usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-dvips.def)
No file about.aux.
[1] (./about.aux) )
Output written on about.dvi (1 page, 736 bytes).
Transcript written on about.log.

LaTeX 会输出许多文本,这样你就可以知道它在干什么。若你的文档包含错误, LaTeX 会报错并提示它可以做什么。大多数情况下,你可以在提示后输入 exit 来强制退出 LaTeX 。

如果用 LaTeX 成功生成一个文档,会生成一个带 .dvi 后缀的文件。DVI 表示 “ 设备无关 Device Independent ”,因为你可以使用不同的工具来生成其他格式。例如, dvipdf 程序可以将 DVI 文件转换为 PDF 文件。

$ dvipdf about.dvi

LaTeX output

添加列表

LaTeX 支持两种列表:一种以数字开头的 “枚举” 列表,一种 “逐项” 或 “项目符号” 列表。在第二段后添加一个简短的枚举列表,列出人们可以参与 Opensource.com 的方式:

\begin{enumerate}
\item Be a writer
\item Be a reader
\end{enumerate}

与在文档定义中添加 \begin\end 声明类似,你也需要在列表前后添加 \begin\end 声明。在列表中,每个项目以 \item 命令开始。当你用 LaTeX 处理该文档并转换为 PDF 格式后,你会看到该列表为数字列表:

LaTeX output

你也可以在列表中嵌套列表。这是一个优雅的功能,如果你需要在列表中为每个条目添加选项。例如,你可以为想要在 Opensource.com 中成为作者的人们提供一些不同的资源。嵌入列表使用单独的 \begin\end 声明。为了看起来方便,我在示例中添加了空行,但是 LaTeX 会忽略这些空行:

\begin{enumerate}
\item Be a writer

  \begin{itemize}
  \item Resources for writers
  \item Contributor Club
  \item Correspondent Program
  \end{itemize}

\item Be a reader
\end{enumerate}

作为嵌套列表,新列表嵌入在编号 1 的项目中,因为你在原先的 \item 声明之间添加了列表。你可以通过在 \end{enumerate} 语句前添加新列表,作为编号 2 项目的嵌套列表。

LaTeX output

章节和小节

你可以将冗长文章分成多个章节,这样更易于阅读。使用 \section{...} 语句在大括号内添加章节标题。例如,你可以在文档顶部添加一个标题为 “About Opensource.com” 的新章节:

$ head about.tex 
\documentclass{article}
\begin{document}

\section{About Opensource.com}

Opensource.com is a premier, daily publication focused on
open source and Linux tutorials, stories, and resources.

We're a diverse and inviting group, made up of staff
editors, Correspondents, contributors, and readers. We

article 文档类会在每个主要章节添加编号,并使字体变大来突出显示。

LaTeX output

你可以使用 \subsection{...} 命令来组织文档。就像 \section{...} 命令一样,在大括号中输入副标题名称。

$ head about.tex
\documentclass{article}
\begin{document}

\section{About Opensource.com}

Opensource.com is a premier, daily publication focused on
open source and Linux tutorials, stories, and resources.

\subsection{Welcome to the Opensource.com community}

LaTeX output

标题和作者

用于出版的科学类的文章需要标题、作者以及发表日期。LaTeX 提供了通过插入命令的方式来添加这些信息,然后使用单独的 \maketitle 命令生成文章的标题。

将 “About Us” 作为文章标题,作者为 “Opensource.com Editors”,发表日期为 “July 10, 2022” 。你必须在 \begin{document} 之后,文章内容前插入这些内容。

\title{About Us}
\author{Opensource.com Editors}
\date{July 10, 2022}
\maketitle

当你在生成文档时,LaTeX 会将标题、作者和日期添加到文章的顶部:

LaTeX output

着重强调

科学和其他技术类文章通常会突出术语和短语。 LaTeX 提供了几种可以在技术文档中使用的字体效果,包括强调文本(通常以斜体显示)、粗体文本和 小型大写字母 small caps

将短语“staff editors, Correspondents, contributors, and readers”放在斜体文本中,并将特定词“reader”和“writer”放在段落后面的强调文本中。你也可以将“skills, talents, backgrounds, and experiences”加粗。虽然这不是正确的样式设置方式,但你可以使用小型大写字母来键入 “Linux” 。

$ head -20 about.tex 
\documentclass{article}
\begin{document}

\title{About Us}
\author{Opensource.com Editors}
\date{July 10, 2022}
\maketitle

\section{About Opensource.com}

Opensource.com is a premier, daily publication focused on
open source and \textsc{Linux} tutorials, stories, and resources.

\subsection{Welcome to the Opensource.com community}

We're a diverse and inviting group, made up of \textit{staff
editors, Correspondents, contributors, and readers}. We
value differences in \textbf{skills, talents, backgrounds, and
experiences}. There are a few different ways to get involved
as a \emph{reader} or a \emph{writer}.

该示例展示了不同样式的文本的应用方法。当你需要强调时,使用 \emph{...} 命令,将强调主题放在大括号内。要以斜体、粗体或小型大写字母显示文本,使用 \text 命令的变体:\textit{...} 用于斜体,\textbf{...} 用于粗体,以及 \ textsc{...} 用于小型大写字母。LaTeX 支持许多其他方式来设置文本样式,这些样式有助于你编写科学技术类文章。

LaTeX output

使用 LaTeX

我只是介绍了使用 LaTeX 撰写科学技术文章的几种方式。你也可以在 LaTeX 中添加脚注,进行数学公式和方程的排版,取决于你的需求。你也可以通过阅读 Opensource.com 中的文章 《在 LaTeX 中创建文档的介绍》 ,了解使用 LaTeX 撰写科学技术文章的其他方式。


via: https://opensource.com/article/22/8/pdf-latex

作者:Jim Hall 选题:lkxed 译者:Donkey 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

本教程介绍了如何实时监控 Linux 日志文件(桌面、服务器或应用)以进行诊断和故障排除。

当你在 Linux 桌面、服务器或任何应用中遇到问题时,你首先会查看单独的日志文件。日志文件通常是文本流和来自应用的带有时间戳的消息。它可以帮助你缩小特定问题的范围,并使你能够找到问题的原因。它还可以帮助从网络获得帮助。

一般来说,所有的日志文件都位于 /var/log。此目录包含特定应用和服务的扩展名为 .log 的日志文件,它还包含了其他含有日志的独立目录。

log files in var-log

所以,如果你想监控一堆日志文件或特定的一个,这里有一些方法可以做到。

Linux 实时监控日志文件

使用 tail 命令

tail 命令是实时跟踪日志文件的最基本方式。特别是如果你在只有终端而没有 GUI 的服务器中。这很有帮助。

基本语法如下:

tail /path/to/log/file

用法:

Monitoring multiple log files via tail

可以使用开关 -f 跟踪实时更新的日志文件。例如,如果要关注 syslog,可以使用以下命令。

tail -f /var/log/syslog

你可以使用单个命令监控多个日志文件:

tail -f /var/log/syslog /var/log/dmesg

如果要监视 HTTP 或 sftp 或任何服务器,可以在此命令中使用它们各自的日志文件。

请记住,上述命令需要管理员权限。

使用 lnav(日志文件浏览器)

lnav Running

lnav 是一个出色的程序,你可以用它来用彩色编码的信息以更有条理的方式监控日志文件。在 Linux 系统中,这个工具不是默认安装的。你可以用下面的命令来安装它:

Ubuntu:

sudo apt install lnav

Fedora:

sudo dnf install lnav

lnav 的好处在于,如果你不想安装它,你可以下载其预编译的可执行文件并在任何地方运行它,甚至可以从 U 盘上运行。无需设置,并加载了功能。使用 lnav,你可以通过 SQL 查询日志文件,以及其他很酷的功能,你可以在其官方网站上学习。

安装后,你可以在具有管理员权限的终端上运行 lnav,它会默认显示 /var/log 中的所有日志并开始实时监控。

关于 systemd 的 journalctl 的一个说明

当今所有现代 Linux 发行版都主要使用 systemd。 systemd 提供了运行 Linux 操作系统的基本框架和组件。 systemd 通过 journalctl 提供日志服务,这有助于管理来自所有 systemd 服务的日志。你还可以使用以下命令实时监控各个 systemd 服务和日志。

journalctl -f

以下是一些特定的 journalctl 命令,可用于多种情况。你可以将这些与上面的 -f 选项结合使用以开始实时监控。

对于紧急系统消息,请使用:

journalctl -p 0

显示带有解释的错误:

journalctl -xb -p 3

使用时间控制过滤:

journalctl --since "2022-12-04 06:00:00"
journalctl --since "2022-12-03" --until "2022-12-05 03:00:00"
journalctl --since yesterday
journalctl --since 09:00 --until "1 hour ago"

如果你想了解更多关于 journalctl 的详细信息,我已经在这写了份 指南

结束语

我希望这些命令和技巧可以帮助你找到桌面或服务器中问题/错误的根本原因。有关更多详细信息,你可以随时参考手册页并使用各种选项。如果你对本文有任何意见或想法,请使用下面的评论栏告诉我。

干杯。


via: https://www.debugpoint.com/monitor-log-files-real-time/

作者:Arindam 选题:lkxed 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

单元测试可能令人生畏,但是这些 Python 模块会使你的生活变得更容易。

在这个教程中,你将学到如何对执行 HTTP 请求代码的进行单元测试。也就是说,你将看到用 Python 对 API 进行单元测试的艺术。

单元测试是指对单个行为的测试。在测试中,一个众所周知的经验法则就是隔离那些需要外部依赖的代码。

比如,当测试一段执行 HTTP 请求的代码时,建议在测试过程中,把真正的调用替换成一个假的的调用。这种情况下,每次运行测试的时候,就可以对它进行单元测试,而不需要执行一个真正的 HTTP 请求。

问题就是,怎样才能隔离这些代码?

这就是我希望在这篇博文中回答的问题!我不仅会向你展示如果去做,而且也会权衡不同方法之间的优点和缺点。

要求:

使用一个天气状况 REST API 的演示程序

为了更好的解决这个问题,假设你正在创建一个天气状况的应用。这个应用使用第三方天气状况 REST API 来检索一个城市的天气信息。其中一个需求是生成一个简单的 HTML 页面,像下面这个图片:

web page displaying London weather

伦敦的天气,OpenWeatherMap。图片是作者自己制作的。

为了获得天气的信息,必须得去某个地方找。幸运的是,通过 OpenWeatherMap 的 REST API 服务,可以获得一切需要的信息。

好的,很棒,但是我该怎么用呢?

通过发送一个 GET 请求到:https://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={api_key}&units=metric,就可以获得你所需要的所有东西。在这个教程中,我会把城市名字设置成一个参数,并确定使用公制单位。

检索数据

使用 requests 模块来检索天气数据。你可以创建一个接收城市名字作为参数的函数,然后返回一个 JSON。JSON 包含温度、天气状况的描述、日出和日落时间等数据。

下面的例子演示了这样一个函数:

def find_weather_for(city: str) -> dict:
    """Queries the weather API and returns the weather data for a particular city."""
    url = API.format(city_name=city, api_key=API_KEY)
    resp = requests.get(url)
    return resp.json()

这个 URL 是由两个全局变量构成:

BASE_URL = "https://api.openweathermap.org/data/2.5/weather"
API = BASE_URL + "?q={city_name}&amp;appid={api_key}&amp;units=metric"

API 以这个格式返回了一个 JSON:

{
  "coord": {
    "lon": -0.13,
    "lat": 51.51
  },
  "weather": [
    {
      "id": 800,
      "main": "Clear",
      "description": "clear sky",
      "icon": "01d"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 16.53,
    "feels_like": 15.52,
    "temp_min": 15,
    "temp_max": 17.78,
    "pressure": 1023,
    "humidity": 72
  },
  "visibility": 10000,
  "wind": {
    "speed": 2.1,
    "deg": 40
  },
  "clouds": {
    "all": 0
  },
  "dt": 1600420164,
  "sys": {
    "type": 1,
    "id": 1414,
    "country": "GB",
    "sunrise": 1600407646,
    "sunset": 1600452509
  },
  "timezone": 3600,
  "id": 2643743,
  "name": "London",
  "cod": 200

当调用 resp.json() 的时候,数据是以 Python 字典的形式返回的。为了封装所有细节,可以用 dataclass 来表示它们。这个类有一个工厂方法,可以获得这个字典并且返回一个 WeatherInfo 实例。

这种办法很好,因为可以保持这种表示方法的稳定。比如,如果 API 改变了 JSON 的结构,就可以在同一个地方(from_dict 方法中)修改逻辑。其他代码不会受影响。你也可以从不同的源获得信息,然后把它们都整合到 from_dict 方法中。

@dataclass
class WeatherInfo:
    temp: float
    sunset: str
    sunrise: str
    temp_min: float
    temp_max: float
    desc: str

    @classmethod
    def from_dict(cls, data: dict) -> "WeatherInfo":
        return cls(
            temp=data["main"]["temp"],
            temp_min=data["main"]["temp_min"],
            temp_max=data["main"]["temp_max"],
            desc=data["weather"][0]["main"],
            sunset=format_date(data["sys"]["sunset"]),
            sunrise=format_date(data["sys"]["sunrise"]),
        )

现在来创建一个叫做 retrieve_weather 的函数。使用这个函数调用 API,然后返回一个 WeatherInfo,这样就可创建你自己的 HTML 页面。

def retrieve_weather(city: str) -> WeatherInfo:
    """Finds the weather for a city and returns a WeatherInfo instance."""
    data = find_weather_for(city)
    return WeatherInfo.from_dict(data)

很好,我们的 app 现在有一些基础了。在继续之前,对这些函数进行单元测试。

1、使用 mock 测试 API

根据维基百科 模拟对象 mock object 是通过模仿真实对象来模拟它行为的一个对象。在 Python 中,你可以使用 unittest.mock 库来 模拟 mock 任何对象,这个库是标准库中的一部分。为了测试 retrieve_weather 函数,可以模拟 requests.get,然后返回静态数据。

pytest-mock

在这个教程中,会使用 pytest 作为测试框架。通过插件,pytest 库是非常具有扩展性的。为了完成我们的模拟目标,要用 pytest-mock。这个插件抽象化了大量 unittest.mock 中的设置,也会让你的代码更简洁。如果你感兴趣的话,我在 另一篇博文中 会有更多的讨论。

好的,言归正传,现在看代码。

下面是一个 retrieve_weather 函数的完整测试用例。这个测试使用了两个 fixture:一个是由 pytest-mock 插件提供的 mocker fixture, 还有一个是我们自己的。就是从之前请求中保存的静态数据。

@pytest.fixture()
def fake_weather_info():
    """Fixture that returns a static weather data."""
    with open("tests/resources/weather.json") as f:
        return json.load(f)
def test_retrieve_weather_using_mocks(mocker, fake_weather_info):
    """Given a city name, test that a HTML report about the weather is generated
    correctly."""
    # Creates a fake requests response object
    fake_resp = mocker.Mock()
    # Mock the json method to return the static weather data
    fake_resp.json = mocker.Mock(return_value=fake_weather_info)
    # Mock the status code
    fake_resp.status_code = HTTPStatus.OK

    mocker.patch("weather_app.requests.get", return_value=fake_resp)

    weather_info = retrieve_weather(city="London")
    assert weather_info == WeatherInfo.from_dict(fake_weather_info)

如果运行这个测试,会获得下面的输出:

============================= test session starts ==============================
...[omitted]...
tests/test_weather_app.py::test_retrieve_weather_using_mocks PASSED      [100%]
============================== 1 passed in 0.20s ===============================
Process finished with exit code 0

很好,测试通过了!但是...生活并非一帆风顺。这个测试有优点,也有缺点。现在来看一下。

优点

好的,有一个之前讨论过的优点就是,通过模拟 API 的返回值,测试变得简单了。将通信和 API 隔离,这样测试就可以预测了。这样总会返回你需要的东西。

缺点

对于缺点,问题就是,如果不再想用 requests 了,并且决定回到标准库的 urllib,怎么办。每次改变 find_weather_for 的代码,都得去适配测试。好的测试是,当你修改代码实现的时候,测试时不需要改变的。所以,通过模拟,你最终把测试和实现耦合在了一起。

而且,另一个不好的方面是你需要在调用函数之前进行大量设置——至少是三行代码。

...
    # Creates a fake requests response object
    fake_resp = mocker.Mock()
    # Mock the json method to return the static weather data
    fake_resp.json = mocker.Mock(return_value=fake_weather_info)
    # Mock the status code
    fake_resp.status_code = HTTPStatus.OK
...

我可以做的更好吗?

是的,请继续看。我现在看看怎么改进一点。

使用 responses

mocker 功能模拟 requests 有点问题,就是有很多设置。避免这个问题的一个好办法就是使用一个库,可以拦截 requests 调用并且给它们 打补丁 patch 。有不止一个库可以做这件事,但是对我来说最简单的是 responses。我们来看一下怎么用,并且替换 mock

@responses.activate
def test_retrieve_weather_using_responses(fake_weather_info):
    """Given a city name, test that a HTML report about the weather is generated
    correctly."""
    api_uri = API.format(city_name="London", api_key=API_KEY)
    responses.add(responses.GET, api_uri, json=fake_weather_info, status=HTTPStatus.OK)

    weather_info = retrieve_weather(city="London")
    assert weather_info == WeatherInfo.from_dict(fake_weather_info)

这个函数再次使用了我们的 fake_weather_info fixture。

然后运行测试:

============================= test session starts ==============================
...
tests/test_weather_app.py::test_retrieve_weather_using_responses PASSED  [100%]
============================== 1 passed in 0.19s ===============================

非常好!测试也通过了。但是...并不是那么棒。

优点

使用诸如 responses 这样的库,好的方面就是不需要再给 requests 打补丁 patch 。通过将这层抽象交给库,可以减少一些设置。然而,如果你没注意到的话,还是有一些问题。

缺点

unittest.mock 很像,测试和实现再一次耦合了。如果替换 requests,测试就不能用了。

2、使用适配器测试 API

如果用模拟让测试耦合了,我能做什么?

设想下面的场景:假如说你不能再用 requests 了,而且必须要用 urllib 替换,因为这是 Python 自带的。不仅仅是这样,你了解了不要把测试代码和实现耦合,并且你想今后都避免这种情况。你想替换 urllib,也不想重写测试了。

事实证明,你可以抽象出执行 GET 请求的代码。

真的吗?怎么做?

可以使用 适配器 adapter 来抽象它。适配器是一种用来封装其他类的接口,并作为新接口暴露出来的一种设计模式。用这种方式,就可以修改适配器而不需要修改代码了。比如,在 find_weather_for 函数中,封装关于 requests 的所有细节,然后把这部分暴露给只接受 URL 的函数。

所以,这个:

def find_weather_for(city: str) -> dict:
    """Queries the weather API and returns the weather data for a particular city."""
    url = API.format(city_name=city, api_key=API_KEY)
    resp = requests.get(url)
    return resp.json()

变成这样:

def find_weather_for(city: str) -> dict:
    """Queries the weather API and returns the weather data for a particular city."""
    url = API.format(city_name=city, api_key=API_KEY)
    return adapter(url)

然后适配器变成这样:

def requests_adapter(url: str) -> dict:
    resp = requests.get(url)
    return resp.json()

现在到了重构 retrieve_weather 函数的时候:

def retrieve_weather(city: str) -> WeatherInfo:
    """Finds the weather for a city and returns a WeatherInfo instance."""
    data = find_weather_for(city, adapter=requests_adapter)
    return WeatherInfo.from_dict(data)

所以,如果你决定改为使用 urllib 的实现,只要换一下适配器:

def urllib_adapter(url: str) -> dict:
    """An adapter that encapsulates urllib.urlopen"""
    with urllib.request.urlopen(url) as response:
        resp = response.read()
    return json.loads(resp)
def retrieve_weather(city: str) -> WeatherInfo:
    """Finds the weather for a city and returns a WeatherInfo instance."""
    data = find_weather_for(city, adapter=urllib_adapter)
    return WeatherInfo.from_dict(data)

好的,那测试怎么做?

为了测试 retrieve_weather, 只要创建一个在测试过程中使用的假的适配器:

@responses.activate
def test_retrieve_weather_using_adapter(
    fake_weather_info,
):
    def fake_adapter(url: str):
        return fake_weather_info

    weather_info = retrieve_weather(city="London", adapter=fake_adapter)
    assert weather_info == WeatherInfo.from_dict(fake_weather_info)

如果运行测试,会获得:

============================= test session starts ==============================
tests/test_weather_app.py::test_retrieve_weather_using_adapter PASSED    [100%]
============================== 1 passed in 0.22s ===============================

优点

这个方法的优点是可以成功将测试和实现解耦。使用 依赖注入 dependency injection 在测试期间注入一个假的适配器。你也可以在任何时候更换适配器,包括在运行时。这些事情都不会改变任何行为。

缺点

缺点就是,因为你在测试中用了假的适配器,如果在实现中往适配器中引入了一个 bug,测试的时候就不会发现。比如说,往 requests 传入了一个有问题的参数,像这样:

def requests_adapter(url: str) -> dict:
    resp = requests.get(url, headers=<some broken headers>)
    return resp.json()

在生产环境中,适配器会有问题,而且单元测试没办法发现。但是事实是,之前的方法也会有同样的问题。这就是为什么不仅要单元测试,并且总是要集成测试。也就是说,要考虑另一个选项。

3、使用 VCR.py 测试 API

现在终于到了讨论我们最后一个选项了。诚实地说,我也是最近才发现这个。我用 模拟 mock 也很长时间了,而且总是有一些问题。VCR.py 是一个库,它可以简化很多 HTTP 请求的测试。

它的工作原理是将第一次运行测试的 HTTP 交互记录为一个 YAML 文件,叫做 cassette。请求和响应都会被序列化。当第二次运行测试的时候,VCT.py 将拦截对请求的调用,并且返回一个响应。

现在看一下下面如何使用 VCR.py 测试 retrieve_weather

@vcr.use_cassette()
def test_retrieve_weather_using_vcr(fake_weather_info):
    weather_info = retrieve_weather(city="London")
    assert weather_info == WeatherInfo.from_dict(fake_weather_info)

天呐,就这样?没有设置?@vcr.use_cassette() 是什么?

是的,就这样!没有设置,只要一个 pytest 标注告诉 VCR 去拦截调用,然后保存 cassette 文件。

cassette 文件是什么样?

好问题。这个文件里有很多东西。这是因为 VCR 保存了交互中的所有细节。

interactions:
- request:
    body: null
    headers:
      Accept:
      - '*/*'
      Accept-Encoding:
      - gzip, deflate
      Connection:
      - keep-alive
      User-Agent:
      - python-requests/2.24.0
    method: GET
    uri: https://api.openweathermap.org/data/2.5/weather?q=London&appid=<YOUR API KEY HERE>&units=metric
  response:
    body:
      string: '{"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":800,"main":"Clear","description":"clearsky","icon":"01d"}],"base":"stations","main":{"temp":16.53,"feels_like":15.52,"temp_min":15,"temp_max":17.78,"pressure":1023,"humidity":72},"visibility":10000,"wind":{"speed":2.1,"deg":40},"clouds":{"all":0},"dt":1600420164,"sys":{"type":1,"id":1414,"country":"GB","sunrise":1600407646,"sunset":1600452509},"timezone":3600,"id":2643743,"name":"London","cod":200}'
    headers:
      Access-Control-Allow-Credentials:
      - 'true'
      Access-Control-Allow-Methods:
      - GET, POST
      Access-Control-Allow-Origin:
      - '*'
      Connection:
      - keep-alive
      Content-Length:
      - '454'
      Content-Type:
      - application/json; charset=utf-8
      Date:
      - Fri, 18 Sep 2020 10:53:25 GMT
      Server:
      - openresty
      X-Cache-Key:
      - /data/2.5/weather?q=london&amp;units=metric
    status:
      code: 200
      message: OK
version: 1

确实很多!

真的!好的方面就是你不需要留意它。VCR.py 会为你安排好一切。

优点

现在看一下优点,我可以至少列出五个:

  • 没有设置代码。
  • 测试仍然是分离的,所以很快。
  • 测试是确定的。
  • 如果你改了请求,比如说用了错误的 header,测试会失败。
  • 没有与代码实现耦合,所以你可以换适配器,而且测试会通过。唯一有关系的东西就是请求必须是一样的。

缺点

再与模拟相比较,除了避免了错误,还是有一些问题。

如果 API 提供者出于某种原因修改了数据格式,测试仍然会通过。幸运的是,这种情况并不经常发生,而且在这种重大改变之前,API 提供者通常会给他们的 API 提供不同版本。

另一个需要考虑的事情是 就地 in place 端到端 end-to-end 测试。每次服务器运行的时候,这些测试都会调用。顾名思义,这是一个范围更广、更慢的测试。它们会比单元测试覆盖更多。事实上,并不是每个项目都需要使用它们。所以,就我看来,VCR.py 对于大多数人的需求来说都绰绰有余。

总结

就这么多了。我希望今天你了解了一些有用的东西。测试 API 客户端应用可能会有点吓人。然而,当武装了合适的工具和知识,你就可以驯服这个野兽。

我的 Github 上可以找到这个完整的应用。

这篇文章最早发表在 作者的个人博客,授权转载


via: https://opensource.com/article/21/9/unit-test-python

作者:Miguel Brito 选题:lujun9972 译者:Yufei-Yan 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用我最喜欢的工具在 Linux 上截屏,可以节省时间。

在写开源软件时,我更喜欢展示一些截图来帮助演示我在说什么。古语有云,一图胜千言。如果你能展示一件事,那通常比试图用言语描述它要好。

有几种方法可以在 Linux 中截图。以下是我在 Linux 上用于捕获截图的三种方法:

1、GNOME

GNOME 有一个很棒的内置截图工具。只需按下键盘上的 PrtScr 键,GNOME 就会显示一个截图对话框:

Image of GNOME screenshot tool

默认操作是抓取区域的截图。这是一种在你制作截图时裁剪截图的非常有用的方法。只需将高亮显示框移动到你需要的位置,然后使用“抓取”角来更改大小。或选择其他图标之一以截取整个屏幕或系统上的单个窗口。点击“圆圈”图标进行截图,类似于手机上的“拍照”按钮。 GNOME 截图工具将截图保存在图片文件夹内的截图文件夹中。

2、GIMP

如果你需要更多截图选项,你可以使用流行的图像编辑器 GIMP 截图。要进行截图,请选择“ 文件 File ”并选择“ 创建 Create ”子菜单,然后选择“ 截图 Screenshot ”。

Image of the GIMP screenshot menu

该对话框允许你截取单个窗口、整个屏幕或仅一个区域的屏幕截图。我喜欢这个工具可以让你设置一个延迟:选择窗口后多长时间,按下截图后多长时间。当我想截取菜单操作的截图时,我经常使用此功能,因此我有足够的时间去窗口打开菜单。

GIMP 将截图作为新图像打开,你可以对其进行编辑并保存到你喜欢的位置。

3、Firefox

如果你需要截取网站的截图,请尝试使用 Firefox 的内置截图程序。右键单击网页正文中的任意位置,然后从菜单中选择“ 截图 Take Screenshot ”:

Image of screenshot utility

Firefox 切换到模态显示,并提示你单击或拖动页面以选择区域,或使用其中一个图标保存整个页面的副本,或仅在浏览器中可见的内容:

Image of Firefox modal display

当你在屏幕上移动鼠标时,你可能会注意到 Firefox 会高亮显示某些区域。这些是页面上的块元素,例如 <div> 或其他块元素。单击该元素以对其进行截图。 Firefox 将截图保存到你的下载文件夹,或你设置为“下载”位置的任何位置。

如果你尝试记录流程,那么截图可以为你节省大量时间。

尝试使用其中一种方法在 Linux 上截图。

(图片来源:Jim Hall,CC BY-SA 40)


via: https://opensource.com/article/22/8/screenshots-linux

作者:Jim Hall 选题:lkxed 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出