测试开发技术:Python测试框架Pytest的基础入门

Posted 爱吃 香菜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了测试开发技术:Python测试框架Pytest的基础入门相关的知识,希望对你有一定的参考价值。

Pytest简介

Pytest is a mature full-featured Python testing tool that helps you write better programs.The pytest framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries.

通过官方网站介绍我们可以了解到,Pytest是一个非常成熟的全功能的Python测试框架,主要有以下几个特点:

简单灵活易上手

支持参数化

支持简单的单元测试和复杂的功能测试,还可以用来做自动化测试

具有很多第三方插件,并且可以自定义扩展

测试用例的skip和xfail处理

可以很好的和Jenkins集成

支持运行由Nose、UnitTest编写的测试用例

Pytest安装

直接使用pip命令安装:

pip install -U pytest    # -U是如果已安装会自动升级最新版本

验证安装结果:

pytest --version    # 展示当前安装版本

C:\\Users\\edison>pytest --version
pytest 6.2.5

在Pytest测试框架中,要遵循以下约束:

测试文件名要符合test_.py或_test.py格式(例如test_min.py);

测试类要以Test开头,且不能带有init方法;

在单个测试类中,可以包含一个或多个test_开头的函数。

Pytest测试执行

Pytest进行测试比较简单,我们来看一个实例:

import pytest    # 导入pytest包

def test_001():    # 函数以test_开头
    print("test_01")

def test_002():
    print("test_02")

if __name__ == '__main__':
    pytest.main(["-v","test_1214.py"])    # 调用pytest的main函数执行测试

这里我们定义了两个测试函数,直接打印出结果,下面执行测试:

============================= test session starts =============================
platform win32 -- Python 3.8.0, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- D:\\Code\\venv\\Scripts\\python.exe
cachedir: .pytest_cache
rootdir: D:\\Code
collecting ... collected 2 items

test_1214.py::test_001 PASSED                                            [ 50%]
test_1214.py::test_002 PASSED                                            [100%]

============================== 2 passed in 0.11s ==============================

Process finished with exit code 0

输出结果中显示执行了多少条案例、对应的测试模块、通过条数以及执行耗时。

测试类主函数

pytest.main(["-v","test_1214.py"])

通过Python代码执行pytest.main():

1.直接执行pytest.main() 【自动查找当前目录下,以test_开头的文件或者以_test结尾的py文件】。

2.设置Pytest的执行参数 pytest.main([‘–html=./report.html’,‘test_login.py’])【执行test_login.py文件,并生成html格式的报告】。

main()括号内可传入执行参数和插件参数,通过[]进行分割,[]内的多个参数通过“逗号,”进行分割:

运行目录及子包下的所有用例 :pytest.main([‘目录名’]);

运行指定模块所有用例 :pytest.main([‘test_reg.py’]);

运行指定模块指定类指定用例 :pytest.main([‘test_reg.py::TestClass::test_method’]) 冒号分割。

-m=xxx: 运行打标签的用例

-reruns=xxx:失败重新运行

-q: 安静模式, 不输出环境信息

-v: 丰富信息模式, 输出更详细的用例执行信息

-s: 显示程序中的print/logging输出

–resultlog=./log.txt 生成log

–junitxml=./log.xml 生成xml报告

断言方法

Pytest断言主要使用Python原生断言方法,主要有以下几种:

== 内容和类型必须同时满足相等

in 实际结果包含预期结果

is 断言前后两个值相等

import pytest    # 导入pytest包

def add(x,y):    # 定义以test_开头函数
    return x + y

def test_add():
    assert add(1,2) == 3    # 断言成功

str1 = "Python,Java,Ruby"
def test_in():
    assert "php" in str1    # 断言失败

if __name__ == '__main__':
    pytest.main(["-v","test_pytest.py"])    # 调用main函数执行测试
============================= test session starts =============================
platform win32 -- Python 3.8.0, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- D:\\Code\\venv\\Scripts\\python.exe
cachedir: .pytest_cache
rootdir: D:\\Code
collecting ... collected 2 items

test_pytest.py::test_add PASSED                                          [ 50%]
test_pytest.py::test_in FAILED                                           [100%]

================================== FAILURES ===================================
___________________________________ test_in ___________________________________

    def test_in():
>       assert "PHP" in str1
E       AssertionError: assert 'PHP' in 'Python,Java,Ruby'

test_pytest.py:11: AssertionError
=========================== short test summary info ===========================
FAILED test_pytest.py::test_in - AssertionError: assert 'PHP' in 'Python,Java...
========================= 1 failed, 1 passed in 0.18s =========================

Process finished with exit code 0

可以看到运行结果中明确指出了错误原因是“AssertionError”,因为PHP不在str1中。

常用命令详解

运行指定案例:

if __name__ == '__main__':
    pytest.main(["-v","-s","test_1214.py"])

运行当前文件夹包括子文件夹所有用例:

if __name__ == '__main__':
    pytest.main(["-v","-s","./"])

运行指定文件夹(code目录下所有用例):

if __name__ == '__main__':
    pytest.main(["-v","-s","code/"])

运行模块中指定用例(运行模块中test_add用例):

if __name__ == '__main__':
    pytest.main(["-v","-s","test_pytest.py::test_add"])

执行失败的最大次数。使用表达式"–maxfail=num"来实现(注意:表达式中间不能存在空格),表示用例失败总数等于num 时停止运行。


错误信息在一行展示。在实际项目中如果有很多用例执行失败,查看报错信息将会很麻烦。

使用"–tb=line"命令,可以很好解决这个问题。

接口调用

本地写一个查询用户信息的接口,通过Pytest来调用,并进行接口断言。

# -*- coding: utf-8 -*-
import pytest
import requests

def test_agent():
    r = requests.post(
        url="http://127.0.0.1:9000/get_user",
        data=
            "name": "吴磊",
            "sex": 1
        ,
        headers="Content-Type": "application/json"
    )
    print(r.text)
    assert r.json()['data']['retCode'] == "00" and r.json()['data']['retMsg'] == "调用成功"

if __name__ == "__main__":
    pytest.main(["-v","test_api.py"])

如果你不想一个人野蛮生长,找不到系统的资料,问题得不到帮助,坚持几天便放弃的感受的话,可以加入我们的QQ群:746506216,大家可以一起讨论交流,里面会有各种软件测试资料和技术交流。


资源分享

下方这份完整的软件测试视频学习教程已经上传CSDN官方认证的二维码,朋友们如果需要可以自行免费领取 【保证100%免费】

Python - pytest

参考技术A

目录

pytest是Python的单元测试框架,同自带的unittest框架类似,但pytest框架使用起来更简洁,效率更高。

pytest特点

安装

测试

在测试之前要做的准备

我的演示脚本处于这样一个的目录中:

踩坑:你创建的pytest脚本名称中不允许含有 . ,比如 1.简单上手.py ,这样会报错。当然,可以这么写 1-简单上手.py

demo1.py :

上例中,当我们在执行(就像Python解释器执行普通的Python脚本一样)测试用例的时候, pytest.main(["-s", "demo1.py"]) 中的传参需要是一个元组或者列表(我的pytest是5.2.2版本),之前的版本可能需要这么调用 pytest.main("-s demo1.py") ,传的参数是str的形式,至于你使用哪种,取决于报不报错:

遇到上述报错,就是参数需要一个列表或者元组的形式,而我们使用的是str形式。

上述代码正确的执行结果是这样的:

大致的信息就是告诉我们:

pytest.main(["-s", "demo1.py"])参数说明

除了上述的函数这种写法,也可以有用例类的写法:

用法跟unittest差不多,类名要以 Test 开头,并且其中的用例方法也要以 test 开头,然后执行也一样。

执行结果:

那么,你这个时候可能会问,我记得unittest中有setup和teardown的方法,难道pytest中没有嘛?你怎么提都不提?稳住,答案是有的。

接下来,我们来研究一下pytest中的setup和teardown的用法。

我们知道,在unittest中,setup和teardown可以在每个用例前后执行,也可以在所有的用例集执行前后执行。那么在pytest中,有以下几种情况:

来一一看看各自的用法。

模块级别setup_module/teardown_module

执行结果:

类级别的setup_class/teardown_class

执行结果:

类中方法级别的setup_method/teardown_method

执行结果:

函数级别的setup_function/teardown_function

执行结果:

小结

该脚本有多种运行方式,如果处于PyCharm环境,可以使用右键或者点击运行按钮运行,也就是在pytest中的主函数中运行:

也可以在命令行中运行:

这种方式,跟使用Python解释器执行Python脚本没有什么两样。也可以如下面这么执行:

当然,还有一种是使用配置文件运行,来看看怎么用。

在项目的根目录下,我们可以建立一个 pytest.ini 文件,在这个文件中,我们可以实现相关的配置:

那这个配置文件中的各项都是什么意思呢?

首先, pytest.ini 文件必须位于项目的根目录,而且也必须叫做 pytest.ini 。

其他的参数:

OK,来个示例。

首先,(详细目录参考开头的目录结构)在 scripts/test_case_01.py 中:

在 scripts/test_case_dir1/test_case02.py 中:

那么,在不同的目录或者文件中,共有5个用例将被执行,而结果则是两个失败三个成功。来执行验证一下,因为有了配置文件,我们在终端中(前提是在项目的根目录),直接输入 pytest 即可。

由执行结果可以发现, 2 failed, 3 passed ,跟我们的预期一致。

后续执行相关配置都来自配置文件,如果更改,会有相应说明,终端都是直接使用 pytest 执行。

我们知道在unittest中,跳过用例可以用 skip ,那么这同样是适用于pytest。

来看怎么使用:

跳过用例,我们使用 @pytest.mark.skipif(condition, reason) :

然后将它装饰在需要被跳过用例的的函数上面。

效果如下:

上例执行结果相对详细,因为我们在配置文件中为 addopts 增加了 -v ,之前的示例结果中,没有加!
另外,此时,在输出的控制台中, 还无法打印出 reason 信息,如果需要打印,则可以在配置文件中的 addopts 参数的 -s 变为 -rs :

如果我们事先知道测试函数会执行失败,但又不想直接跳过,而是希望显示的提示。

Pytest 使用 pytest.mark.xfail 实现预见错误功能::

需要掌握的必传参数的是:

那么关于预期失败的几种情况需要了解一下:

结果如下:

pytest 使用 x 表示预见的失败(XFAIL)。

如果预见的是失败,但实际运行测试却成功通过,pytest 使用 X 进行标记(XPASS)。

而在预期失败的两种情况中,我们不希望出现预期失败,结果却执行成功了的情况出现,因为跟我们想的不一样嘛,我预期这条用例失败,那这条用例就应该执行失败才对,你虽然执行成功了,但跟我想的不一样,你照样是失败的!

所以,我们需要将预期失败,结果却执行成功了的用例标记为执行失败,可以在 pytest.ini 文件中,加入:

这样就就把上述的情况标记为执行失败了。

pytest身为强大的单元测试框架,那么同样支持DDT数据驱动测试的概念。也就是当对一个测试函数进行测试时,通常会给函数传递多组参数。比如测试账号登陆,我们需要模拟各种千奇百怪的账号密码。

当然,我们可以把这些参数写在测试函数内部进行遍历。不过虽然参数众多,但仍然是一个测试,当某组参数导致断言失败,测试也就终止了。

通过异常捕获,我们可以保证程所有参数完整执行,但要分析测试结果就需要做不少额外的工作。

在 pytest 中,我们有更好的解决方法,就是参数化测试,即每组参数都独立执行一次测试。使用的工具就是 pytest.mark.parametrize(argnames, argvalues) 。

使用就是以装饰器的形式使用。

只有一个参数的测试用例

来看(重要部分)结果::

可以看到,列表内的每个手机号,都是一条测试用例。

多个参数的测试用例

(重要部分)结果:

可以看到,每一个手机号与每一个验证码都组合一起执行了,这样就执行了4次。那么如果有很多个组合的话,用例数将会更多。我们希望手机号与验证码一一对应组合,也就是只执行两次,怎么搞呢?

在多参数情况下,多个参数名是以 , 分割的字符串。参数值是列表嵌套的形式组成的。

固件(Fixture)是一些函数,pytest 会在执行测试函数之前(或之后)加载运行它们,也称测试夹具。

我们可以利用固件做任何事情,其中最常见的可能就是数据库的初始连接和最后关闭操作。

Pytest 使用 pytest.fixture() 定义固件,下面是最简单的固件,访问主页前必须先登录:

结果:

在之前的示例中,你可能会觉得,这跟之前的setup和teardown的功能也类似呀,但是,fixture相对于setup和teardown来说更灵活。pytest通过 scope 参数来控制固件的使用范围,也就是作用域。

比如之前的login固件,可以指定它的作用域:

很多时候需要在测试前进行预处理(如新建数据库连接),并在测试完成进行清理(关闭数据库连接)。

当有大量重复的这类操作,最佳实践是使用固件来自动化所有预处理和后处理。

Pytest 使用 yield 关键词将固件分为两部分, yield 之前的代码属于预处理,会在测试前执行; yield 之后的代码属于后处理,将在测试完成后执行。

以下测试模拟数据库查询,使用固件来模拟数据库的连接关闭:

结果:

可以看到在两个测试用例执行前后都有预处理和后处理。

pytest中还有非常多的插件供我们使用,我们来介绍几个常用的。

先来看一个重要的,那就是生成测试用例报告。

想要生成测试报告,首先要有下载,才能使用。

下载

如果下载失败,可以使用PyCharm下载,怎么用PyCharm下载这里无需多言了吧。

使用

在配置文件中,添加参数:

效果很不错吧!

没完,看我大招

Allure框架是一个灵活的轻量级多语言测试报告工具,它不仅以web的方式展示了简洁的测试结果,而且允许参与开发过程的每个人从日常执行的测试中最大限度的提取有用信息。
从开发人员(dev,developer)和质量保证人员(QA,Quality Assurance)的角度来看,Allure报告简化了常见缺陷的统计:失败的测试可以分为bug和被中断的测试,还可以配置日志、步骤、fixture、附件、计时、执行 历史 以及与TMS和BUG管理系统集成,所以,通过以上配置,所有负责的开发人员和测试人员可以尽可能的掌握测试信息。
从管理者的角度来看,Allure提供了一个清晰的“大图”,其中包括已覆盖的特性、缺陷聚集的位置、执行时间轴的外观以及许多其他方便的事情。allure的模块化和可扩展性保证了我们总是能够对某些东西进行微调。

少扯点,来看看怎么使用。

Python的pytest中allure下载

但由于这个 allure-pytest 插件生成的测试报告不是 html 类型的,我们还需要使用allure工具再“加工”一下。所以说,我们还需要下载这个allure工具。

allure工具下载

在现在allure工具之前,它依赖Java环境,我们还需要先配置Java环境。

注意,如果你的电脑已经有了Java环境,就无需重新配置了。

配置完了Java环境,我们再来下载allure工具,我这里直接给出了百度云盘链接,你也可以去其他链接中自行下载:

下载并解压好了allure工具包之后,还需要将allure包内的 bin 目录添加到系统的环境变量中。

完事后打开你的终端测试:

返回了版本号说明安装成功。

使用

一般使用allure要经历几个步骤:

来看配置 pytest.ini :

就是 --alluredir ./report/result 参数。

在终端中输入 pytest 正常执行测试用例即可:

执行完毕后,在项目的根目下,会自动生成一个 report 目录,这个目录下有:

接下来需要使用allure工具来生成HTML报告。

此时我们在终端(如果是windows平台,就是cmd),路径是项目的根目录,执行下面的命令。

PS:我在pycharm中的terminal输入allure提示\'allure\' 不是内部或外部命令,也不是可运行的程序或批处理文件。但windows的终端没有问题。

命令的意思是,根据 reportresult 目录中的数据(这些数据是运行pytest后产生的)。在 report 目录下新建一个 allure_html 目录,而这个目录内有 index.html 才是最终的allure版本的HTML报告;如果你是重复执行的话,使用 --clean 清除之前的报告。

结果很漂亮:

allure open
默认的,allure报告需要HTTP服务器来打开,一般我们可以通过pycharm来完成,另外一种情况就是通过allure自带的open命令来完成。

allure的其他用法
当然,故事还是没有完!在使用allure生成报告的时候,在编写用例阶段,还可以有一些参数可以使用:

allure.title与allure.description

feature和story

由上图可以看到,不同的用例被分为不同的功能中。

allure.severity

allure.severity 用来标识测试用例或者测试类的级别,分为blocker,critical,normal,minor,trivial5个级别。

severity的默认级别是normal,所以上面的用例5可以不添加装饰器了。

allure.dynamic

在之前,用例的执行顺序是从上到下依次执行:

正如上例的执行顺序是 3 1 2 。

现在,来看看我们如何手动控制多个用例的执行顺序,这里也依赖一个插件。

下载

使用

手动控制用例执行顺序的方法是在给各用例添加一个装饰器:

那么, 现在的执行顺序是 2 1 3 ,按照order指定的排序执行的。

如果有人较劲传个0或者负数啥的,那么它们的排序关系应该是这样的:

失败重试意思是指定某个用例执行失败可以重新运行。

下载

使用

需要在 pytest.ini 文件中, 配置:

给 addopts 字段新增(其他原有保持不变) --reruns=3 字段,这样如果有用例执行失败,则再次执行,尝试3次。

来看示例:

结果:

我们也可以从用例报告中看出重试的结果:

上面演示了用例失败了,然后重新执行多少次都没有成功,这是一种情况。

接下来,来看另一种情况,那就是用例执行失败,重新执行次数内通过了,那么剩余的重新执行的次数将不再执行。

通过 random 模块帮助我们演示出在某次执行中出现失败的情况,而在重新执行的时候,会出现成功的情况,看结果:

可以看到,用例 02 重新执行了一次就成功了,剩余的两次执行就终止了。

一条一条用例的执行,肯定会很慢,来看如何并发的执行测试用例,当然这需要相应的插件。

下载

使用

在配置文件中添加:

就是这个 -n=auto :

并发的配置可以写在配置文件中,然后其他正常的执行用例脚本即可。另外一种就是在终端中指定,先来看示例:

结果:

pytest-sugar 改变了 pytest 的默认外观,添加了一个进度条,并立即显示失败的测试。它不需要配置,只需 下载插件即可,用 pytest 运行测试,来享受更漂亮、更有用的输出。

下载

其他照旧执行用例即可。

pytest-cov 在 pytest 中增加了覆盖率支持,来显示哪些代码行已经测试过,哪些还没有。它还将包括项目的测试覆盖率。

下载

使用

在配置文件中:

也就是配置 --cov=./scripts ,这样,它就会统计所有 scripts 目录下所有符合规则的脚本的测试覆盖率。

执行的话,就照常执行就行。

结果:

更多插件参考:https://zhuanlan.zhihu.com/p/50317866

有的时候,在 pytest.ini 中配置了 pytest-html 和 allure 插件之后,执行后报错:

出现了这个报错,检查你配置的解释器中是否存在 pytest-html 和 allure-pytest 这两个模块。如果是使用的pycharm ide,那么你除了检查settings中的解释器配置之外,还需要保证运行脚本的编辑器配置是否跟settings中配置一致。

以上是关于测试开发技术:Python测试框架Pytest的基础入门的主要内容,如果未能解决你的问题,请参考以下文章

Python - pytest

Python接口自动化测试之pytest与unittest区别

Python接口自动化测试之pytest与unittest区别

python测试框架:pytest如何实现数据传参

pytest接口自动化测试框架 | 用python代码测试接口

Pytest测试框架知识