一、单元测试框架简介

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. 深入Typescript--01-使用roolup编译Typescript

    Typescript是什么? TypeScript是Javascript的超集,遵循最新的ES5/ES6规范.Typescript扩展了Javascript语法. 为什么要用Typescript? 1 ...

  2. 假如你想在VUE的main.js里根据条件按需引入注册组件以及样式,那就这样子写,附赠自己写的vue一个框架配置多系统按需加载系统路由以及组件办法

    假如你想在VUE的main.js里根据条件按需引入注册组件以及样式,那就这样子写 举例来说我想要引入大屏的一些组件,但是原来框架已经集成了多个项目,路由也是按需加载的,想要实现组件按需加载 先在mai ...

  3. ApiView/Request类源码分析/序列化器

    内容概要 ApiView+JsonResponse编写接口 ApiView+Response编写接口 ApiView源码解析 Request对象源码分析 序列化器介绍和快速使用/反序列化 反序列化的校 ...

  4. Kubernetes(k8s)配置文件管理:ConfigMap

    目录 一.系统环境 二.前言 三.ConfigMap概览 四.创建ConfigMap 五.ConfigMap的使用 5.1 以环境变量的方式使用ConfigMap 5.2 以卷的方式使用ConfigM ...

  5. Dubbo-RPC核心接口介绍

    前言 Dubbo源码阅读分享系列文章,欢迎大家关注点赞 SPI实现部分 Dubbo-SPI机制 Dubbo-Adaptive实现原理 Dubbo-Activate实现原理 Dubbo SPI-Wrap ...

  6. CentOS7安装了图形界面为默认如何修改默认登录到控制台

    在安装的时候,选择了图形界面安装,一段时间后,想还是直接登录到控制台,需要的时候在手动登录到图形界面, 在CentOS7中的设置方法不同与之前的版本 在之前的版本中是修改配置文件 sudo nano ...

  7. CodeSmith 简单使用和常用模板

    1.简介 CodeSmith 是一种基于模板的代码生成工具,它使用类似于 ASP.NET的语法来生成任意类型的代码或文本. 2.软件布局 整体布局和visual studio系列相似,用过VS开发对此 ...

  8. Spring(Spring的读取外部资源- p 命名空间)

    Spring读取外部资源 实际开发中,数据库的资源一般会单独保存起来.一般会保存到后缀为properties的文件中,方便维护和修改,如果Spring加载资源,就需要在spring.xml中读取pro ...

  9. metasploit2-practice

    Metasploittable2打靶教程 本次靶机练习主要熟悉:高危端口利用:metasploit中search,show及各个模块使用. 一.环境准备 1.把靶场放在vmware打开,启用nat模式 ...

  10. 新一代自动化利器-DrissionPage

    熟悉的小伙伴知道我的工作有相当一部分是自动化,在探索相关的技术上一直没停下脚步,我痛恨selenium.playwright的非标准内核机制,也曾对clicknium引进了新的问题无语,以及接口爬取数 ...