现实中类似测试的场景有很多,比如飞机和地铁的安检、电脑和手机的测试等等
注:第三方包一般拥有自己的社区(一般是 GitHub),当你在调用它们时遇到问题后,除了通过搜索引擎搜索,还可以试试直接在社区中反馈或提问。
命令中的 `python -m pip` 让 Python 运行 pip 模块,`install --upgrade` 会让 pip 更新一个已安装的包,最后的 `pip` 则指定了要更新哪个第三方包,也即自己更新自己。
在输出中,`-` 后面显示的数字,就是版本号,可以看到在制作讲义时的最新版是 23.2.1,旧版本则是 23.1.2。
由于网络原因,执行过程可能需要一些时间,安装其他三方库也会如此,如果需要等待,则不妨沏一壶小茶休息一下(当然你也可以回忆回忆之前的知识。)
注1:这里为了防止大家出错,命令用的是 python -m pip,实际上一般可以使用 pip 或 pip3 命令(即 pip install –upgrade pip)。如果你发现使用 pip 或 pip3 命令来执行没有问题,则可以替代使用。
注2:如果你使用的是 Linux,在安装 Python 时可能不会自动安装 pip。如果在你试图更新 pip 时出现错误消息,请参阅附录 A 提供的说明。
如果在执行这个命令时遇到麻烦,可尝试在不指定标志 --user 的情况下再次执行它。
(关于人为测试相关内容,详见原书同章节部分)
注:书中对于全覆盖测试的描述是比较笼统的,测试也如同本页所说,实际上是一门学问,不妨可以在以后继续了解和学习。
测试文件的名称很重要,当你让 pytest 运行测试时,它将查找以 test_ 开头的文件。
然后,定义一个测试函数 test_first_last_name() ,这个函数有几点需要注意的:
第一,测试函数也必须以 test 为首。在测试过程中,pytest 将找出并运行所有以 test 为首的函数。
第二,测试函数的名称应该比典型的函数名更长,更具描述性。
你自己不会调用测试函数,而是由 pytest 替你查找并运行它们。因此,测试函数的名称应足够长,让你在测试报告中看到它们时,能清楚地知道它们测试的是哪些行为。
强调注意:
1. 测试函数必须以 test 为首。在测试过程中,pytest 将找出并运行所有以 test 为首的函数。
2. 测试函数的名称应足够长,让你在测试报告中看到它们时,能清楚地知道它们测试的是哪些行为。毕竟你不会自己调用测试函数,而是由 pytest 替你查找并运行它们。
结果值:一般来自于被测试的函数的返回值。
预期值:可以是根据北侧函数代码推出的值,也可以是通过人为测试得到的符合预期的函数结果。
如果忘了如何在终端窗口中切换到正确的文件夹,请参阅 1.5 节,简单来说:
在 windows 中,可以通过 `pushd <文件夹路径>` 来切换到指定的路径,例如 pushd d:/code
在 linux 中,则使用 cd 命令(windows 事实上也带有 cd 命令,但还是建议你使用 pushd 命令)
注:如果你使用类似与 Pycharm 等软件时(IDE,集成开发环境),在打开文件后,可能就能看到测试函数旁有运行按钮,点击它将自动为你执行测试;不过我们用的是 VS Code,其本质是一款编辑器,额外的功能都是由插件提供的,对于这一章,我们并不会推荐任何相关插件。不过在初学时多熟悉一些终端命令,总不会是坏事。
下面来尝试解读这些输出。首先,我们看到了一些有关运行测试的系统的信息。
我是在 Linux 系统中运行该测试的,因此你看到的输出可能与这里显示的不同(书中是在 MacOS 中运行的),这一行后面的输出,指出了用来运行该测试的 Python、pytest 和其他相关包的版本。
注1:和之前的 pip 命令一样,为了防止大家出错,此处的命令用的是完整的 python -m pytest,而不是简单直接的 pytest,如果你发现 pytest 执行没有问题,则可以替代使用。
注2:终端是否能显示彩色取决于终端本身,你所用的终端可能不会像图片一样带有丰富的颜色。
接下来,可以看到该测试是从哪个目录运行的,这里是 /home/scruel/code。
如你所见,pytest 找到了一个测试,并指出了运行的是哪个测试文件。
文件名后面的点号表明有一个测试通过了,而 100% 指出运行了所有的测试。
在可能有数百乃至数千个测试的大型项目中,点号和完成百分比有助于监控测试的运行进度。
最后一行指出有一个测试通过了,运行该测试花费的时间不到 0.01 秒。
上述输出表明,在给定包含名和姓的姓名时,get_formatted_name() 函数总是能正确地处理它。
修改 get_formatted_name() 后,可再次运行测试。如果它通过了,就表明在给定 Janis Joplin 这样的姓名时,这个函数依然能够正确地处理。
这个版本应该能够正确地处理包含中间名的姓名,但对其进行测试时,我们发现它不再能正确地处理只有名和姓的姓名了。
这里的信息很多,因为在测试未通过时,需要你知道的事情可能有很多。
首先,输出中有一个字母 F,表明有一个测试未通过。
然后是 FAILURES 部分,这是关注的焦点,因为在运行测试时,通常应该关注未通过的测试。
在这里,pytest 指出未通过的测试函数是 test_first_last_name()
右尖括号(>)为首的那行,指出了导致测试未能通过的代码
以 E 为首的下一行中,指出了导致测试未通过的具体错误:缺少必不可少的位置实参 'last‘,导致 TypeError。
空一行后接下来的一行,指出了导致测试未通过的文件名,以及涉及的代码行数。
在末尾的简短小结中,再次列出了最重要的信息。
这样,即使你运行了很多测试,也可快速获悉哪些测试未通过以及测试未通过的原因。
如果向这个函数传递了中间名,姓名将包含名、中间名和姓,否则将只包含名和姓。
现在,对于这两种不同的姓名,这个函数应该都能够正确地处理了。
如果向这个函数传递了中间名,姓名将包含名、中间名和姓,否则将只包含名和姓。
现在,对于这两种不同的姓名,这个函数应该都能够正确地处理了。
我们将这个新函数命名为 test_first_last_middle_name(),再次强调,函数名必须以 test 为首,这样该函数才会在我们运行 pytest 时被自动运行 。
函数名清楚地指出了它测试的是 get_formatted_name() 的哪个行为,如果该测试未通过,我们就能马上知道受影响的是哪种类型的姓名。
两个点号表明有两个通过的测试,最后一行输出文字也清楚地指出了这一点。
现在我们知道,这个函数又能正确地处理像 Janis Joplin 这样的姓名了,而且能确定它还能够正确地处理像 Wolfgang Amadeus Mozart 这样的姓名。
注:原书中的“确信其中没有错误”是相对来说的,毕竟我们编写的测试代码也是有可能有错误的,或者可能会是不全面的。
这里只是列出了一部分内容,大家可以回顾一下之前第 5 章中提及的条件测试等相关内容,事实上断言后面放的,就是条件测试。
这个类首先存储一个调查问题,并创建了一个空列表,用于存储答案。
这个类包含在答案列表中添加新答案的方法,以及将存储在列表中的答案打印出来的方法。要创建这个类的实例,只需提供一个问题即可。有了表示调查的实例,就可以使用 show_question() 来显示其中的问题,使用 store_response() 来存储答案,并使用 show_results() 来显示调查结果了。
注:限于版面,这里对原书中的类进行了简化,去掉了 show_question 这个方法,这一修改不会影响测试的结果。
要创建这个类的实例,只需提供一个问题即可。
有了表示调查的实例,就可以使用 store_response() 来存储答案,并使用 show_results() 来显示调查结果了。
对于这个测试函数,一个不错的描述性名称是 test_store_single_response()。
如果这个测试未通过,我们就能通过测试小结中的函数名得知,在存储单个调查答案方面存在问题。
我们将这个新函数命名为 test_store_three_responses(),并像 test_store_single_response() 一样,在其中创建一个调查对象。
然后定义一个包含三个不同答案的列表,再对其中的每个答案都调用 store_response()。
存储这些答案后,使用一个循环来断言每个答案都包含在 language_survey.responses 中。
这里则引出了一个代码编写过程中的简单原则,即 DRY
直译为:不要重复你自己,意思是不要通过复制粘贴,重复编写一样的代码,避免以后修改时要“牵一发而动全身”。
注1:夹具这个词有点怪,不过为了迎合原书就保留了,实际上个人感觉用“固定装置”的直译更好。
注2:装饰器本质上其实是一个特殊的函数或类,我们暂时不需要学习如何编写它,目前只需要学会如何使用即可。
请注意,测试函数的定义有变化:增加了一个名为 language_survey 的形参。
我们删除了测试函数中的两行代码,没有新增代码,删除的分别是:
定义问题字符串的代码行
创建 AnonymousSurvey 对象的代码行
当测试函数的一个形参与应用了装饰器 @pytest.fixture 的函数同名时,将自动运行这个同名的夹具,并将夹具返回的值传递给测试函数。
在这个示例中,language_survey() 函数向 test_store_single_response() 提供了一个 language_survey 实例。
两个测试函数的定义都变了,且两个测试函数都没有新增代码,而且都删除了两行代码:定义问题的代码行, 以及创建 AnonymousSurvey 对象的代码行。
如果要扩展 AnonymousSurvey,使其允许 每个用户输入多个答案,这些测试将很有用:修改代码以接受多个答案后,你可运行这些测试, 确认存储单个答案或一系列答案的行为未受影响。
推荐阅读:《人月神话》和《人件》等软件工程相关书籍,你或许能更好地理解测试的作用。