一、单元测试框架简介

1. 什么是单元测试

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

2. 常用单元测试框架

2.1 Java 类别

  • junit

  • testng

2.2 Python 类别

  • unittest

  • pytest

3. 单元测试框架主要作用

  • 测试发现:从多个文件中找到测试用例

  • 测试执行:按照一定的顺序和规则去执行用例,并生成结果

  • 测试判断:通过断言判断预期结果和实际结果的差异

  • 测试报告:统计测试进度、耗时、通过率,生成测试报告

二、自动化测试框架简介

1. 什么是自动化测试框架

自动化测试框架是指为了完成一个指定的系统的自动化测试而封装的一整套的完成的代码。主要封装了自动化的基础模块、管理模块、测试的统计模块等等。

2. 自动化测试框架的作用

  • 提高测试效率,降低维护成本

  • 减少人工干预,提高测试的准确性,增加代码的重用性

  • 核心思想是让不懂代码的人也能够通过这个框架去实现自动化测试

3. 单元测试框架和自动化测试框架的关系

单元测试框架只是自动化测试框架中的组成部分之一。一套完整的自动化测试框架还包括:pom设计模式、数据驱动、关键字驱动、全局配置文件的封装、日志监控、类似selenium和requests等功能模块的二次封装、断言系统、测试报告邮件等许多方面。

三、Pytest 简介

pytest 是一个非常成熟的 Python 单元测试框架,比 unittest 更加灵活,功能也更加强大。使用 pytest 主要有以下优点:

  • 可以和 selenium,requests,appium 结合实现 web 自动化,接口自动化,app 自动化;

  • 可以实现测试用例的跳过和失败用例重跑机制;

  • 可以和 allure 结合生成非常美观的测试报告;

  • 可以和 jenkins 持续集成;

  • 有很多强大的插件:

    • pytest:指定用例执行方式

    • pytest-html:生成 html 格式的自动化测试报告

    • pytest-xdist:测试用例分布执行,多 CPU 分发

    • pytest-ordering:用于改变测试用例的执行顺序

    • pytest-rerunfailures:用例失败后重跑

框架和插件的下载:

1 # 直接通过 pip 下载
2 pip install pytest
3 pip install pytest-html
4 pip install pytest-xdist
5 pip install pytest-ordering
6 pip install pytest-rerunfailures

四、Pytest 的使用

1. 默认规则

在使用 pytest 框架时,需要遵循一些默认的规则(规则不是固定的,可以根据全局配置文件进行规则的修改):

  • 模块名必须以 test_ 开头或者以 _test 结尾;

  • 测试类必须以 Test 开头,并且不能有 init 方法;

  • 测试方法必须以 test 开头

如下我们创建一套测试环境,其中 cases 目录为自定义目录,用于存放所有的测试用例:

2. 测试用例的执行

构造如下测试项目:

其中各个用例模块的代码内容为:

 1 # product_cases/test_product.py
2 def test_out_case():
3 print("模拟测试用户退出操作")
4 ​
5
6 # user_cases/test_login.py
7 def test_other_case():
8 print("模拟测试其他情况")
9 ​
10 class TestLogin:
11 ​
12 def test_case_001(self):
13 print("模拟测试用户登录操作")
14
15 ​
16 # user_cases/test_logout.py
17 def test_out_case():
18 print("模拟测试用户退出操作")

pytest 用例的执行主要有以下三种方式:

  • 通过主函数执行

  • 通过命令模式执行

  • 通过读取 pytest.ini 配置文件执行

2.1 通过主函数执行

2.1.1 执行所有用例

我们修改一下用户登录的用例:

 1 # user_cases/test_login.py
2 # 导入pytest
3 import pytest
4 ​
5 ​
6 def test_other_case():
7 print("模拟测试其他情况")
8 ​
9 ​
10 class TestLogin:
11 ​
12 def test_case_001(self):
13 print("模拟测试用户登录操作")
14 ​
15 ​
16 if __name__ == '__main__':
17 pytest.main() # 执行测试用例

此时直接执行:

从终端的打印可以看到,用例文件中的用例方法 test_other_case 以及测试类中的测试方法 test_case_001 都被执行了。

我们再来修改一下产品操作的用例,并执行看看效果:

1 # product_cases/test_product.py
2 import pytest
3 ​
4 def test_product_case():
5 print("模拟测试产品相关的操作")
6 ​
7 ​
8 if __name__ == '__main__':
9 pytest.main()

可见,只需要在对应的用例文件中调用主函数,就可以自动执行文件中的所有用例。但是在实际的使用中,我们一般会将用例分类写在不同目录下的不同用例文件里,且执行时要批量去执行所有的用例,所以我们往往会将主函数写在一个与用例文件夹平级的 py 文件中,pytest 内部设有对应的机制,会自动扫描全局,执行所有的用例文件。

1 # all.py
2 import pytest
3 ​
4 if __name__ == '__main__':
5 pytest.main()

执行 all.py 文件,从终端输出可以看到,所有的用例都被执行了:

2.1.2 运行指定模块
1 # all.py
2 import pytest
3 ​
4 if __name__ == '__main__':
5 pytest.main(["./user_cases/test_logout.py"]) # 只运行用户退出操作用例

2.1.3 运行指定目录
1 # all.py
2 import pytest
3 ​
4 if __name__ == '__main__':
5 pytest.main(["./user_cases"]) # 只运行用户目录下的所有用例

2.1.4 通过nodeid指定用例运行

注:nodeid 由模块名、分隔符、类名、方法名、函数名组成

1 # all.py
2 import pytest
3 ​
4 if __name__ == '__main__':
5 # 只运行用户登录用例模块中的 test_other_case 测试方法
6 pytest.main(["-vs", "./user_cases/test_login.py::test_other_case"])
7 # 只运行用户登录用例模块中的 TestLogin 测试类中的 test_case_001 测试方法
8 pytest.main(["-vs", "./user_cases/test_login.py::TestLogin::test_case_001"])

2.2 通过命令模式去执行

2.2.1 执行所有用例

直接通过 pytest 命令就可以执行当前目录下所有的测试用例,所以要想执行项目中所有的用例,只需要将执行目录切换到根目录即可:

2.2.2 运行指定模块
pytest ./product_cases/test_product.py

2.2.3 运行指定的目录
pytest ./product_cases

2.2.4 通过nodeid指定用例运行
pytest ./user_cases/test_login.py::test_other_case
pytest ./user_cases/test_login.py::TestLogin::test_case_001

2.3 通过配置文件去执行

在真正的项目中,都是通过配置全局的配置文件来执行测试用例的。其余两种执行方式在编写和调试用例时比较方便。对于配置文件,有以下几点要求:

  • 配置文件的名字必须为 pytest.ini

  • 配置文件一般放置在项目的根目录

  • 配置文件必须时 ANSI 编码

pytest.ini 是 pytest 框架的核心配置文件,它可以改变 pytest 的默认行为,不管是通过主函数还是命令模式去执行用例,都会先去读取配置文件。

 1 # pytest.ini
2 [pytest]
3 # 命令行参数,多个参数用空格隔开
4 addopts = -vs --reruns=2
5 # 测试用例文件夹,可自己配置
6 testpaths = ./user_cases
7 # 配置测试搜索的模块名称,如下表示搜索的模块必须以test_开头
8 python_files = test_*.py
9 # 配置测试搜索的测试类名,如下表示搜索的测试类必须以Test开头
10 python_classes = Test*
11 # 配置测试搜索的测试函数名
12 python_functions = test

修改完配置后,再去执行用例时就会按照配置上要求去执行:

3. 执行时使用参数

我们在执行用例时,可以通过添加相关的参数实现不同的执行效果,提升测试效率。

  • - s:用于输出调试信息,包括打印输出的信息

1 # 通过main函数执行
2 pytest.main(["-s", "./product_cases/test_product.py"])
3 # 通过命令模式执行
4 pytest -s ./product_cases/test_product.py

  • -v:显示更详细的信息

1 # 通过main函数执行
2 pytest.main(["-v", "./product_cases/test_product.py"])
3 # 通过命令模式执行
4 pytest -v ./product_cases/test_product.py

  • -vs:将 -v 和 -s 参数效果结合,推荐使用

  • -n:支持多线程或者分布式运行测试用例

1 # 通过main函数执行
2 # 开两个线程去执行用户操作目录下所有的用例
3 pytest.main(["-vs", "./user_cases", "-n=2"])
4
5 # 通过命令模式执行
6 pytest -vs ./user_cases -n 2

为了方便看出测试效果,我们给用户操作目录下的每个测试方法中都添加一个睡眠延时:

 1 # user_cases/test_login.py
2 import time
3
4 def test_other_case():
5 time.sleep(2)
6 print("模拟测试其他情况")
7
8 class TestLogin:
9
10 def test_case_001(self):
11 time.sleep(2)
12 print("模拟测试用户登录操作")
13
14
15 # user_cases/test_logout.py
16 import time
17
18 def test_out_case():
19 time.sleep(2)
20 print("模拟测试用户退出操作")

正常执行结果:

开启线程执行结果:

  • --reruns:失败用例重跑

1 # 通过main函数执行
2 # 如果用例执行失败,该用例再执行2次
3 pytest.main(["-vs", "./user_cases", "--reruns=2"])
4
5 # 通过命令模式执行
6 pytest -vs ./user_cases --reruns 2

为了方便测试重跑机制,我们将用户操作用例中的 test_other_case 测试方法添加一个断言,使其报错

1 # user_cases/test_login.py
2 def test_other_case():
3 print("模拟测试其他情况")
4 assert 1 == 2 # 执行到此时报错,该用例不会通过
5
6 class TestLogin:
7
8 def test_case_001(self):
9 print("模拟测试用户登录操作")

  • -x:只要有一个用例失败,就停止执行

1 # 通过main函数执行
2 pytest.main(["-vs", "./user_cases", "-x"])
3
4 # 通过命令模式执行
5 pytest -vs ./user_cases -x

  • --maxfail:出现失败的用例数达到规定值时停止测试

1 # 通过main函数执行
2 # 失败的用例数达到两个时停止测试
3 pytest.main(["-vs", "./user_cases", "--maxfail=2"])
4
5 # 通过命令模式执行
6 pytest -vs ./user_cases --maxfail 2
  • -k:根据测试用例的部分字符串指定用例执行

1 # 通过main函数执行
2 # 只执行用例名字中包含01的用例
3 pytest.main(["-vs", "./user_cases", "-k=01"])
4
5 # 通过命令模式执行
6 pytest -vs ./user_cases --k="01"

如果指定的字符串是被测试类包含的,那么测试类中所有的测试方法都会被执行。

  • --html:用例执行完成后生成 html 格式的测试报告

执行完成后,在本地的 report 文件夹中生成了对应的测试报告,打开可以看到用例执行的详情。

4. 用例执行顺序

我们再往 test_product.py 中添加一些测试方法:

 1 # product_cases/test_product.py
2 def test_product_case():
3 print("模拟测试产品相关的操作")
4
5
6 def test_case_002():
7 print("测试方法_002")
8
9
10 def test_case_003():
11 print("测试方法_003")
12
13
14 def test_case_001():
15 print("测试方法_001")
16
17
18 def test_case_004():
19 print("测试方法_004")
20
21
22 def test_case_005():
23 print("测试方法_005")

执行该测试模块:

从执行的打印可以看出来,pytest 对于用例的执行顺序是按照从上到下执行的,并不像 unittest 那样是按照用例名的 ASCII 大小来执行的,当然我们也是有方法可以改变用例的执行顺序的。

 1 # product_cases/test_product.py
2 import pytest
3
4 def test_product_case():
5 print("模拟测试产品相关的操作")
6
7 # 通过装饰器方法 @pytest.mark.run() 可以改变用例的执行顺序
8 # order参数表示该用例要排在第几个执行
9 @pytest.mark.run(order=2)
10 def test_case_002():
11 print("测试方法_002")
12
13
14 @pytest.mark.run(order=3)
15 def test_case_003():
16 print("测试方法_003")
17
18
19 @pytest.mark.run(order=1)
20 def test_case_001():
21 print("测试方法_001")
22
23
24 def test_case_004():
25 print("测试方法_004")
26
27
28 def test_case_005():
29 print("测试方法_005")

从终端输出的结果可以看到,被装饰器装饰的测试用例都按照了指定的顺序执行,没有被装饰到的用例还是按照从上到下的顺序执行。

5. 分组执行用例

在实际的测试业务中,我们常常需要去测试某些模块中的某些用例,例如冒烟测试、分模块执行、分接口和web执行等测试场景中就常常涉及到这种需求。pytest 提供了十分便捷的分组执行用例的方式。

  • 第一步,使用装饰器去装饰要执行的用例,@pytest.mark.name,其中 name 为自定义的名称

 1 #################### product_cases/test_product.py ######################
2 import pytest
3
4 def test_product_case():
5 print("模拟测试产品相关的操作")
6
7 def test_case_002():
8 print("测试方法_002")
9
10 def test_case_003():
11 print("测试方法_003")
12
13 @pytest.mark.product_manage
14 def test_case_001():
15 print("测试方法_001")
16
17 @pytest.mark.smoke
18 def test_case_004():
19 print("测试方法_004")
20
21 @pytest.mark.product_manage
22 def test_case_005():
23 print("测试方法_005")
24
25
26 ################## user_cases/test_login.py #######################
27 import pytest
28
29 def test_other_case():
30 print("模拟测试其他情况")
31
32 class TestLogin:
33 @pytest.mark.user_manage
34 def test_case_001(self):
35 print("模拟测试用户登录操作")
36
37
38 ################### user_cases/test_logout.py ########################
39 def test_out_case():
40 print("模拟测试用户退出操作")
  • 第二步,去 pytest.ini 中添加分组配置,配置名必须和装饰器中的 name 值一致

 1 [pytest]
2 addopts = -vs --reruns=2
3 testpaths = .
4 python_files = test_*.py
5 python_classes = Test*
6 python_functions = test
7 # 分组配置
8 markers =
9 smoke:冒烟用例
10 user_manage:用户管理用例
11 product_manage:商品管理用例
  • 第三步,执行时通过参数 -m 指定要执行的用例的分组类别

pytest -m "smoke"

6. 跳过用例

对于某些我们不想执行的用例,可以使用 pytest 提供的装饰器方法跳过该用例不去执行。

6.1 无条件跳过

装饰器语法:@pytest.mark.skip(reason="") ,reason 参数可选,表示跳过该测试方法的原因

 1 #################### product_cases/test_product.py ######################
2 import pytest
3
4 @pytest.mark.skip(reason="无意义的测试方法")
5 def test_product_case():
6 print("模拟测试产品相关的操作")
7
8
9 def test_case_001():
10 print("测试方法_001")
11
12
13 def test_case_002():
14 print("测试方法_002")

6.2 有条件跳过

装饰器语法:@pytest.mark.skipif(判断条件, reason="") ,reason 参数可选,表示跳过该测试方法的原因

 1 import pytest
2
3 num = 20
4
5 @pytest.mark.skip(reason="无意义的测试方法")
6 def test_product_case():
7 print("模拟测试产品相关的操作")
8
9 @pytest.mark.skipif(num > 18, reason="超过限定条件")
10 def test_case_001():
11 print("测试方法_001")
12
13 def test_case_002():
14 print("测试方法_002")

Pytest初识的更多相关文章

  1. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  2. 初识Hadoop

    第一部分:              初识Hadoop 一.             谁说大象不能跳舞 业务数据越来越多,用关系型数据库来存储和处理数据越来越感觉吃力,一个查询或者一个导出,要执行很长 ...

  3. python学习笔记(基础四:模块初识、pyc和PyCodeObject是什么)

    一.模块初识(一) 模块,也叫库.库有标准库第三方库. 注意事项:文件名不能和导入的模块名相同 1. sys模块 import sys print(sys.path) #打印环境变量 print(sy ...

  4. 初识IOS,Label控件的应用。

    初识IOS,Label控件的应用. // // ViewController.m // Gua.test // // Created by 郭美男 on 16/5/31. // Copyright © ...

  5. UI篇(初识君面)

    我们的APP要想吸引用户,就要把UI(脸蛋)搞漂亮一点.毕竟好的外貌是增进人际关系的第一步,我们程序员看到一个APP时,第一眼就是看这个软件的功能,不去关心界面是否漂亮,看到好的程序会说"我 ...

  6. Python导出Excel为Lua/Json/Xml实例教程(一):初识Python

    Python导出Excel为Lua/Json/Xml实例教程(一):初识Python 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出 ...

  7. 初识SpringMvc

    初识SpringMvc springMvc简介:SpringMVC也叫Spring Web mvc,属于表现层的框架.Spring MVC是Spring框架的一部分,是在Spring3.0后发布的 s ...

  8. 初识redis数据类型

    初识redis数据类型 1.String(字符串) string是redis最基本的类型,一个key对应一个value. string类型是二进制安全的.意思是redis的string可以包含任何数据 ...

  9. Redis初识、设计思想与一些学习资源推荐

    一.Redis简介 1.什么是Redis Redis 是一个开源的使用ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库,并提供多种语言的API.从2010 年 ...

  10. MongoDB【第一篇】MongodDB初识

    NoSQL介绍 一.NoSQL简介 NoSQL,全称是”Not Only Sql”,指的是非关系型的数据库. 非关系型数据库主要有这些特点:非关系型的.分布式的.开源的.水平可扩展的. 原始的目的是为 ...

随机推荐

  1. SpringCloud NetFlix学习

    SpringCloud NetFlix 遇到记录不完全的可以看看这个人的博客 学相伴SpringCloud 微服务架构的4个核心问题? 服务很多,客户端该怎么访问? 负载均衡.反向代理,用户请求的永远 ...

  2. Java集合 Map 集合 与 操作集合的工具类: Collections 的详细说明

    Java集合 Map 集合 与 操作集合的工具类: Collections 的详细说明 每博一文案 别把人生,输给心情 师父说:心情不是人生的全部,却能左右人生的全部. 你有没有体会到,当你心情好的时 ...

  3. 记一次使用gdb诊断gc问题全过程

    原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介 上次解决了GC长耗时问题后,系统果然平稳了许多,这是之前的文章<GC耗时高,原因竟是服务流量小?> 然 ...

  4. Nacos服务注册原理分析

    在分布式服务中,原来的单体服务会被拆分成一个个微服务,服务注册实例到注册中心,服务消费者通过注册中心获取实例列表,直接请求调用服务. 服务是如何注册到注册中心,服务如果挂了,服务是如何检测?带着这些问 ...

  5. 论文翻译:2022_Time-Shift Modeling-Based Hear-Through System for In-Ear Headphones

    论文地址:基于时移建模的入耳式耳机透听系统 引用格式: 摘要 透传(hear-through,HT)技术是通过增强耳机佩戴者对环境声音的感知来主动补偿被动隔离的.耳机中的材料会减少声音 500Hz以上 ...

  6. 如何在 C# 项目中链接一个文件夹下的所有文件

    在 C# 项目中通过链接方式引入文件可以让我们在项目中使用这些文件中的代码.常见的比如链接 AssemblyInfo.cs 文件,这样我们就可以在项目中使用这个文件中的版本号等信息.但是如果我们想要链 ...

  7. 为什么要使用 chmod 777

    如上图所示, 不使用sudo,报错没有权限 使用sudo,报错找不到命令 只好chmod 777一下了

  8. vscode 配置复盘

        第一句话,看文档!code.visualstudio.com/docs/editor- 从这里开始看,上下辐射看完debug看task,然后再看其他的诸如"智能感知"    ...

  9. vue @click的stop和prevent

    @click.stop 阻止事件冒泡 @click.prevent 阻止事件的默认行为 联合饿了吗UI使用的时候,el-table(主表)包含一个或多个子表时(el-tabs),点击右侧的编辑.删除时 ...

  10. 视觉SLAM:滑动窗口

    1.SLAM问题 1.1建模 考虑某个状态 \(\xi\),以及一次与该变量相关的观测 \(r_{i}\).由于噪声存在,观测服从概率分布 \(p(r_{i}|\xi)\).多个观测时,各个测量值相互 ...