Pytest 测试框架三部曲(一)

Pytest 测试框架三部曲(二)

Pytest 测试框架三部曲(三)

Unittest 单元测试框架(一)

Unittest 单元测试框架(二)

Selenium 框架三部曲(一)

Selenium 框架三部曲(二)

1. 背景介绍

1.1 工具与框架

工具

它是一种用于完成特定的任务的软件或硬件,他们通常是独立的,可以直接使用。不需要太多的结构或依赖关系,工具本身已经提供了一些功能或方法供用户使用。

例如:Postman、Jmeter 等

框架

它是一种更为完整的软件结构,它提供了一套规范、结构、功能,通常包含一些预定义的规则,用户需要按照框架的规范来组织代码。以测试框架为例,它通常是不直接提供特定的测试能力,你需要通过扩展或者安装第三方的工具库来实现。

例如:unittest、pytest、robot framework 等

1.2 单元测试框架和自动化测试框架

单元测试框架是指在软件开发中,对软件的最小单元(函数 / 方法)进行正确性的检查测试。

单元测试框架的主要工作:测试发现、测试执行、测试判断和测试报告。

单元测试框架和自动化测试框架的区别:

  • 单元测试框架:主要用于对软件中的单个模块或函数进行测试,它通常关注代码的内部结构和逻辑。单元测试的目的是确保每个单元在独立的情况下能够正常工作,并且符合预期的行为。
  • 自动化测试框架:是一种更广泛的测试框架,它可以用于各种类型的测试,如功能测试、性能测试、接口测试等。自动化框架通常会提供一些工具和机制,以便编写和执行测试用例,并管理测试过程。

常见的自动化测试框架:

  • Python:unittest、pytest、robot framework(前两个框架主要是聚焦于白盒单元测试,而 robot framework 主要聚焦于系统测试)
  • Java:Gunit、TestNG

2. Pytest 介绍

2.1 Pytest 介绍

Pytest 官方文档:https://docs.pytest.org/en/latest/

pytest 是一个非常成熟的全功能的 Python 测试框架,支持自动检索自动、参数化、跳过、标记预期等功能,且可以通过安装插件进行功能的扩展,与 Python 自带的 unittesst 测试框架类似,但是比 unittest 框架使用起来更简洁,功能更强大!

pytest 可以用来做系统测试的自动化,它的特点有:

  • 用 Python 编写测试用例,简便易用,比 unittest 更灵活,并且文档也很丰富
  • 可以用文件系统目录层次对应手工测试用例层次结构
  • 支持参数化,可以灵活挑选测试用例执行,并且可以实现测试用例的跳过以及 reruns 重试失败用例
  • 能够支持简单的单元测试和复杂的功能测试
  • 灵活的初始化清除机制
  • 可以和 allure 生成非常美观的测试报告,也可以和 Jenkins 持续集成
  • 支持运行由 nose、unittest 编写的测试 case
  • 支持分布式、多线程跑用例
  • 可以很好的和 CI 工具结合
  • 丰富的插件及社区支持,pytest 有很多强大的插件,可以自定义扩展,并且这些插件能够实现很多实用的操作
    • pytest-html:生成 html 格式的自动化测试报告
    • pytest-xdist:测试用例分布式执行,多 CPU 分发
    • pytest-ordering:用于改变测试用例的执行顺序
    • pytest-rerunfailures:用例失败后重跑
    • allure-pytest:用于生成美观的测试报告

2.2 Pytest 与 Unittest 的区别

unittest pytest
用例编写规则 1. 测试文件必须先 import unittest
2. 测试类必须继承 unittest.TestCase
3. 测试方法必须以 test_ 开头
4. 测试类必须要有 unittest.main() 方法
1. 测试文件名必须以 test_ 开头或者 _test 结尾(例如:test_ab.py)
2. 测试方法必须以 test_ 开头
3. 测试类命名以 Test 开头
用例分类执行 默认执行全部用例,也可以用过加载 testsuit 执行部分用例 可以通过 @pytest.mark 来标记类和方法,pytest.main 加入参数(”-m”)可以只运行标记的类和方法
用例前置和后置 提供了 setUp/tearDown,只能针对所有用例 pytest 中的 fixture 显然更加灵活。可以任意自定义方法函数,只要加上 @pytest.fixture() 这个装饰器,那么被装饰的方法就可以被使用
参数化 需依赖 ddt 库 使用 @pytest.mark.parametrize 装饰器
断言 很多断言格式(assertEqual、assertIn、asserTrue 等) 只有 assert 一个表达式,用起来比较方便
报告 使用 HTMLTestRunner 库 有 pytest-HTML、allure 插件
失败重跑 无此功能 pytest 支持用例执行失败重试,使用 pytest-rerunfailures 插件

2.3 为何用 pytest?

  1. 任何测试框架,最原始的写法:

    • 没有测试报告
    • 不好维护
  2. 大部分开发语言本身提供了自带的测试框架,例如 unittest:

    • 规范了用例代码的写法
    • 一个用例用一个方法概括
    • 提供了方便执行与统计的功能
    • 官方提供自带的,用起来功能不是很强大,没那么方便
  3. 当前流行的测试框架 - pytest

    • 不是 python 自带的,需要自行安装。
1
pip install pytest
  • pytest 和 unittest 相比,更加的规范和宽松,函数定义只需 test 开头或结尾即可。
1
pytest test_app_01.py
  • unittest 能做的,pytest 也能做,并且更强大。

2.4 Pytest 应用

pytest 本质很简单,它只判断 def 函数中是否会报错或者 assert 断言不通过,不管里面内容如何。

因此,pytest 是自动化测试的基石,我们可以用 pytest 实现不同场景下的测试。

例如下面代码,上面为 UI 的自动化测试代码,而下面的则为接口自动化测试代码,但是它们都是基于 pyest 的。

它们只需在函数中通过断言判断结果是否符合预期即可,如果报错则 pytest 就会识别出来,并返回该用例不通过。

Pytest 插件列表:https://docs.pytest.org/en/latest/reference/plugin_list.html

所以这就出现了 pytest 与各种框架的组合,例如:pytest + selenium(UI / web 自动化)、pytest + requests(HTTP / 接口自动化)、pytest + appium(AppUI / APP 自动化)、pytest + websocket 等,什么类别的自动化代码取决于你在 def 函数中的内容。

3. Pytest 快速上手

3.1 安装

直接执行如下命令即可安装 pytest:

1
pip install pytest

安装后可以验证是否安装成功:

1
pytest --version

但是,我们还需要安装很多插件,如果一个个安装就比较麻烦,我们可以在根目录下创建一个 requirements.txt 文件,来存放所有需要安装的插件。

注意: 前面讲解的部分命令中涉及到了下面的插件,所以推荐都事先安装好避免报错~

1
2
3
4
5
6
7
pytest
pytest-html
pytest-xdist
pytest-ordering
pytest-rerunfailures
allure-pytest
flaky

Pytest 插件列表:https://docs.pytest.org/en/latest/reference/plugin_list.html

然后通过如下的命令来下载该文件中的所有插件。

1
pip install -r requirements.txt

运行原理

pytest 如何知道你哪些代码是自动化的测试用例?官方文档给出了 pytest 寻找测试项的具体规则:

  • 如果未指定命令行参数,则从 testpath(如果已配置)或当前目录开始收集;如果命令行参数,指定了目录、文件名或 node id 的任何组合,则按参数来找。
  • 寻找过程会递归到目录中,除非它们匹配上 norecursedirs。
  • 在这些目录中,搜索由其测试包名称导入的 test_*.py 或 *_test.py 文件。
  • 从这些文件中,收集如下测试项:
    • test 为前缀的函数
    • Test 为前缀的类里面的 test 为前缀的方法

3.2 测试用例代码

默认命名规则:

  1. 文件和模块名称必须以 test_ 开头或者以 _test 结尾,目录名称可以不遵循该规则。
  2. 测试类名称必须以 Test 开头,且类中不能有 init 方法。
  3. 测试方法必须以 test 开头。
  4. 名称允许包含中文。

比如,我们创建一个 文件名为 test_错误登录.py ,放在目录 workspace\cases\登录 下面。

其中 worksapce 是我们创建的自动化项目根目录,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Test_错误密码:

def test_C001001(self):
print('\n用例C001001')
assert 1 == 1

def test_C001002(self):
print('\n用例C001002')
assert 2 == 2

def test_C001003(self):
print('\n用例C001003')
assert 3 == 2

如果我们把测试用例存放在类中,则类名必须以 Test 为前缀的 类,用例对应的方法必须以 test 为前缀的方法。pytest 中用例的检查点直接用 Python 的 assert 断言,assert 后面的表达式结果为 True ,就是检查点通过;结果为 False ,就是检查点不通过。

3.3 运行测试

3.3.1 命令行的方式

在 terminal 终端,进入到根目录(这里是 workspace),执行命令 pytest 或 python -m pytest。

注意:

最好使用 python -m pytest 的方式执行,因为我们测试用例经常需要导入项目目录的库文件,如果直接使用 pytest 执行会出现 python 解释器搜索不到库文件的问题:ModuleNotFoundError: No module named 'xxxx'。

而使用 python -m pytest 的方式运行 python,则会自动把当前工作目录作为模块的搜索路径,避免了上述问题的发生。

当然也可以指定要测试的目录即 pytest cases,上面代码运行结果如下。

1
2
3
4
5
pytest

python -m pytest

pytest cases

结果显示一共找到了 3 个测试项,其中 2 个执行通过,1 个不通过,并且会显示出执行不通过的用例以及细节。

另外,打印的结果中,文件名称后面的绿色小点表示该条用例通过,红色字母 F 则表示该条用例不通过。

3.3.2 主函数的方式

1
2
if __name__=='__main__'
pytest.main()

在主函数中也可以使用参数,例如下面我想增加重试的机制,在命令行中效果如下:

1
python -m pytest cases -sv --reruns 2 --reruns-delay 1

通过主函数方式执行的话,则效果如下:

1
2
if __name__=='__main__'
pytest.main(['--reruns', '2', '--reruns-delay', '3'])

3.3.3 全局配置文件 pytest.ini 的方式

官网文档:https://docs.pytest.org/en/latest/reference/reference.html#ini-options-ref

pytest.ini 这个文件是 pytest 单元测试框架的核心配置文件。它可以修改 pytest 的默认行为,通过读取配置信息,从而改变 pytest 运行方式,按指定的方式来运行。比如使用 pytest.ini 文件可以破除默认 pattern,命名测试文件、搜索规则等。

  • 位置:一般放在项目的根目录
  • 作用:改变 pytest 默认的行为
  • 运行的规则:不管是主函数的模式运行,还是命令行的模式运行,都会去读取这个配置文件
1
2
3
4
5
6
[pytest]
addopts = -vs #命令行参数,用空格分隔
testpaths = ./cases #测试用例的路径
python_files = test_*.py #模块的命令规则
python_classes = aaa* #类的命名规则
python_functions = aaa #方法的命名规则

现在我们可以修改一下用例中类和方法的名称,然后观察一下按照默认规则运行和修改规则后运行的结果如何。

1
2
3
4
class aaa_错误密码:
def aaa_C001001(self):
print('\n用例C001001')
assert 1 == 1

3.4 参数详解

  • -s

从上面代码中可以发现,存在 print 打印语句,但是终端中并没有打印相关信息,这是因为 pytest 会截获 print 打印的内容。如果希望显示代码中 print 的内容的话,则可以在 pytest 后加上参数 -s。

1
python -m pytest -s

  • -v

如果希望得到更详细的执行信息,包括每个测试类、测试函数的名字,可以加上参数 -v。

1
python -m pytest -sv

  • 其它参数
    • -x:只要有一个用例错误就会停止执行
    • –maxfail NUM:出现 NUM 个用例失败就会停止执行
    • -k:根据测试用例的部分字符串指定测试用例(包括文件名、类名、方法名等)
      • python -m pytest -k "1002":只会执行文件名、类名、方法名等的名称中包含 1002 字符的用例