单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小很明确的功能是否正确。通常而言,一个单元测试是判断某个特定条件(或者场景)下某个特定函数的行为。
单元测试越早对后期的集成测试越有好处
开发者自己负责
单元测试的时候一个大前提就是需要清楚的知道,自己要测试的程序块所预期的输入和输出,然后根据这个预期和程序逻辑来书写 case。这里的预期结果一定要针对需求/设计的逻辑去写,而不是针对程序实现去写,否则单元测试就失去了意义,照着错误的实现设计出的 case 也可能是错的。单元覆盖率
代码覆盖率也被用于自动化测试和手工测试来度量测试是否全面的指标之一,应用覆盖率的思想增强测试用例的设计
待测试的代码片段
def demo_method(a,b,x): if (a>1 and b==0): x=x/a if (a==2 or x>1 x= x+1 return x
语句覆盖
漏洞:
大部分的判定语句是由多个逻辑条件组合而成,若仅仅判断其整个最终结果,而忽视每个取值条件的结果,必然会遗漏部分测试路径
a ==2 or x>1 ---> a==2 or x<1
条件覆盖
测试用例: if (a >1 and b ==0)
缺陷:测试用例指数级增加(2**conditions)
路径覆盖
unittest:Python 内置的标准库。它的 API 跟 Java 的 JUnit,.net 的 NUnit,C++的 CppUnit
pytest:丰富,灵活的测试框架,语法简单,可以结合 allure 生成一个酷炫的测试报告
Nose: unittest 的扩展,使得 python 的测试更加简单
Mock:unittest.mock 是用来测试 python 的库,这个是一个标准库(出现在 python 3.3 以后)
python 自带的单元测试框架,常用在单元测试
在自动化测试中提供用例组织与执行
提供丰富的断言---验证函数等功能
加上 HTMLTestRunner 可以生成 HTML 的报告
unittest 编写规范
各种执行行-单一用例, 全部
import unittest class TestClass(unittest.TestCase): @classmethod def setUpClass(cls): print("这个测试整个类前要执行的方法") def setUp(self): print("这是每一个类创建的方法") def tearDown(cls): print("这是每一个方法后面运行的方法") def test_first(self): print("这是测试方法1") self.assertEqual(1,1) @unittest.skip("这次不想执行这个测试") def test_second(self): print("这是测试方法2") self.assertEqual(1,'1',"1 is not equal '1'") @classmethod def tearDownClass(cls): print("这是测试整个类后要执行的方法") if __name__ == '__main__': unittest.main()
assert 断言
python3---assert 官方文档:https://docs.python.org/3/library/unittest.html#assert-methods
unittest 执行测试用例
多个测试用例的集合就是测试套件,通过测试套件来管理多个测试用例
执行方法 1
unittest.main()
执行方法 2: 加入容器中执行
suite.addTest(TestMethod("test_01")) suite.addTest(TestMethod(("test_02")) unittest.TextRunner().run(suite)
测试方法三:此用法可以同时测试多个类
suite1 = unittest.TestLoader().loadTestsFromTestsFromTestCase(TestCase1) suite2 = unittest.TestLoader().loadTestsFromTestsFromTestCase(TestCase2) suite = unitest.TestSuite([suite1,suite2]) unitest.TextTestRunner(verbosity=2).run(suite)
执行方法四:匹配某个目录下所有以 test 开头的 py 文件,执行这些文件下所有测试用例
test_dir ="./test_case" discover = unittest.defaultTestLoader.discover(test_dir,pattern="test*.py" discover 可以一次调用多个脚本 test_dir 被测试脚本的路径 pattern 脚本名称匹配规则
测试用例执行过程
pytest 框架介绍
pytest 是一个非常成熟的全功能的 python 测试框架
简单灵活,容易上手
支持参数化
测试用例的 skip 和 xfail,自动失败重试
能够支持简单的单元测试和复杂的功能测试,还可以用来做 selenium/appium 等自动化测试,接口自动化测试(pytest +requests)
pytest 具有很多第三方插件,并且可以自定义扩展,比较好的如 pytest-allure
可以很好的和 jenkins 集成
官方文档
文档:Full pytest documentation — pytest documentation
第三方库:Search results · PyPI
pytest 简单练习
# 文件名 test_simple.py def func(x): return x+1 def test_answer(): assert func(3)==5
pytest .\test_simple.py
pytest .\test_simple.py
pip install -U pytest U 表示升级
pip install pytest pytest-sugar
pip install pytest-rerunfailures
pip install pytest-xdist
pip install pytest-assume
pip install pytest-html
pip list 查看
pytest -h 帮助
测试用例的识别与运行
测试文件
用例识别
pytest/py.test pytest -v (最高级别信息--verbose)打印详细运行日志信息 pytest -v -s 文件名(s 是带控制台输出结果,也是详细输出) pytest 文件名.py 执行单独一个 pytest 模块 pytest 文件名.py::类名 运行某个模块里面某个类 pytest 文件名.py:: 类名::方法名 运行某个模块里面的某个类里面的方法 pytest -v -k "类名 and or 方法名" 跳过某个用例 pytest -m [标记名] @ Mark.[标记名] 将运行有这个标记的测试用例 pytest -x 文件名 一旦运行报错就停止运行 pytest --maxfail==[num] 当运行错误达到 num 的时候就停止运行
Pytest 执行--失败重新运行
场景: 测试失败后要重新运行 n 次,要在重新运行之间添加延迟时间,间隔 n 秒再运行
pip install pytest-rerunfailures
pytest -v --reruns 3 -s test_class.py
pytest -v - -reruns 5 --returns-delay 1
多条断言有失败也都运行
场景:一个方法中写多条断言,通常第一条过不去,下面就不执行了。我们想报错也都执行一下。
pip install pytest-assume
pytest.assume(1==4)
pytest.assume(2==4)
测试用例的识别与运行
pycharm 配置与执行 pytest 测试框架
pytest 框架结构
import pytest 类似的 setup, teardown 同样更灵活
模块级(setup_module/ teardown_module)模块始末,全局的(优先级最高)
函数级(setup_function/teardown_function) 只对函数用例生效(不在类中)
类级 (setup_class/teardown_class)只在类中前后运行一次 (在类中)
方法级 (setup_method/teardown_method) 开始于方法始末(在类中)
类里面的(Setup/teardown)运行在调用方法的前后
pytest-fixture 的用法
场景
在方法前面 @pytest.fixture()
例子 1:前端自动化中应用
场景:测试用例执行时,有的用例需要登录才能执行,有些用例不需要登录。setup 和 teardown 无法满足,fixture 可以。,默认 scope function
场景:与其他测试工程师合作一起开发时,公共模块要在不同文件中,要在大家都能访问到的地方
步骤:
将登录模块带 @pytest.fixture 写在 conftest.py
conftest.py 配置需注意:
conftest 文件名是不能换的
conftest.py 与运行的用例要在同一个 package 下,并且有__init__.py
不需要 import 导入 conftest.py ,pytest 用例会自动查找
全局的配置和前期工作都可以写在这里,放到某个包下,就是这个包数据共享的地方
例子 3: 前端自动化中应用-yield
场景:
你已经可以将测试方法前要执行的或的依赖解决了,测试方法后销毁清除数据要如何进行呢?
解决
通过在同一模块中加入 yield 关键字,yield 是调用第一次返回结果,第二次执行它下面的语句返回
步骤
在 @pytest.fixture(scope= module)
在登录的方法中加 yield ,之后加销毁清除的步骤,(这种方法没有返回值,如果希望返回使用 addfinalizer)
fixture 的自动应用
场景
不想源测试方法有任何改动,或全部都自动实现自动应用,每特例,也都不需要返回值时可以选择自动应用
解决
使用 fixture 中的 autouse = True 实现
步骤
在方法上加 @pytest.fixture(autouse = True)
在测试方法上加 @pytest.mark.usefixtures("start")
从结果中可以看到每次测试方法的执行软件都执行了 open 函数
fixture 带参数传递
场景
测试离不开数据,为了数据灵活,一般数据购书通过参数的
解决:fixture 通过固定参数 request 传递
步骤
在 fixture 中增加 @pytest.fixture(params=[1,2,3,'linda']在参数写 request
import pytest
@pytest.mark.parametrize("test_input,expected",[{"3+5",8},("2+5",7),("7+5",30)])
def test_eval(test_input,expected):
assert eval(test_input) == expected
import pytest
test_user_data =['Toma','Jerry']
@pytest.fixture(scope="module")
def login_r(request):
# 这是接收并传入参数
user = request.param
print( f"\n 打开首页准备登录,登录用户:{user}")
return user
indirect = true 可以把传过来的参数当函数来执行
@pytest.mark.parametrize("login_r",test_user_data,indirect=True)
def test_login(login_r):
a = login_r
print(f"测试用例中login的返回值:{a}")
assert a!= ""
skip 跳过
xfail
mark 中的 skip 与 xfail
skip 使用场景
调试是不想运行这个用例
标记无法在某些平台上运行的测试功能
在某些版本中执行,其他版本跳过
当前的外部资源不可用时跳过(如果测试数据是从数据库中取得的,连接数据库的功能如果返回结果未成功就跳过,因此执行也都报错)
解决
@pytest.mark.skip 跳过这个测试用例,可以加上条件 skipif, 在某些条件下才希望通过,否则就跳过这个测试
Xfail 场景
功能测试尚实施或尚未修复的错误,当前测试通过时,尽管预计会失败(标记为 pytest.xfail, 它是一个 xpass ,将在测试摘要中报告
你希望测试由于某种情况而应该失败
解决
@pytest.mark.xfail
使用自定义标记 mark 只执行某部分用例
场景
只执行符合要求的某一部分用例,可以把一个 web 项目划分为多个模块,然后指定模块名称执行
app 自动化是,如果想 Android 和 IOS 共用一套代码时,可以使用标记功能标记那些是 Android 的,那些是 iOS 的运行时指定 Mark 名称即可
解决
在测试用例方法上加上 @pytest.mark.webtest
执行
-s 参数:输出所有测试用的 print 信息
-m :执行自定义标记的相关用例 pytest -s test_mark_zi_09.py
pytest -s test-mark_zi_09.py -m=webtest
pytest -s test_mark_zi_09.py -m apptest
pytest -s test_mark_zi_09.py -m "not ios"
多线程并行执行与分布式执行
场景
测试 1000 条,一个用例执行 1 分钟,一个测试员执行需要 1000 分钟,通常我们会用人力成本换取时间成本,加几个人一起执行,时间就会缩短。如果 10 个人一起执行只需要 100 分钟,这就是一种并行测试,分布式场景
解决
pytest 分布式插件:pytest-xdist,多个 CPU 或主机执行 前提:用例之间是独立的,没有先后顺序,随机都能执行,可重复运行并不影响其他用例
安装:
pip3 install pytest-xdist
多个 CPU 并行执行用例,直接加-n 3 是并行数量 pytest -n 3
在多个终端下一起执行
pytest-html 生成报告
安装
pip imstall pytest-html
https://pypi.org/project/pytest-html/
生成 html 报告
pytest -v -d --html - - self-contained-html
参数化用例
pytest 数据参数化
@python.mark.parametrize (argnames, argvalue)
argnames:要参数化的变量,string(逗号分割) list tuple
使用 string
@pytest.mark.parametrize("a,b",[(10,20),(10,30)]
def test_param(self,a,b):
print(a+b)
使用 list
@pytest.mark.parametrize(["a","b"],[10,20],[10,30])
def test_param(self,a,b):
print(a+b)
使用 tuple
@pytest.mark.parametrize(("a","b"),[10,20],[10,30])
def test_param(self,a,b):
print(a+b)
class Testdata:
# @pytest.mark.parametrize("a,b",[(10,20),(10,5),(3,9)])
# @pytest.mark.parametrize(("a","b"),[(10,20),(10,5),(3,9)])
@pytest.mark.parametrize(["a","b"],[(10,20),(10,5),(3,9)])
# ["a","b"] 可以进行修改
# ("a","b") 可以进行修改
def test_data(self,a,b): # a=10 # b=20 print(a+b)
argvalues: 参数化的值,list, list[tuple]
yaml 的基本使用
yaml 实现 list
list
10
20
30
yaml 实现字典
dict
by:id
locator:name
action:click
yaml 进行嵌套
by:id
-locator:name
yaml 进行更复杂的嵌套
companies:
id:1
name: company1
price:200w
id:2
name:company2
price:500w
companies:[{id:1,name:company,price:200w},{id:2,name:company2,price:500w}]
加载 yaml 文件
yaml.safe_load(open("./data.yaml"))
class Testdata_yaml:
@pytest.mark.parametrize(("a","b"),yaml.safe_load(open("./data.yaml")))
def test_data_yml(self,a,b):
print(a+b)
pip install PyYaml
data.yml
运行结果
数据驱动
简介
数据驱动就是数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变(参数化的应用)
数据量大的测试用例可以使用一种结构 haul 的文件(yml, JSON)来对数据进行存储,然后再测试用例中读取这些数据
简单练习
import pytest
import yaml
class TestDemo:
@pytest.mark.parametrize("env",yaml.safe_load(open("./env.yml")))
def test_demo(self,env):
if "test" in env:
print("这是测试环境")
print("测试环境的IP是:",env["test"])
elif "dev" in env:
print("这是开发环境")
print("开发环境的IP是:",env["dev"])
def test_yaml(self):
print(yaml.safe_load(open("./env.yml")))
应用场景
App, Web,接口自动化测试
测试步骤的数据驱动
测试数据的数据驱动
配置的数据驱动
测试报告美化
Allure 介绍
Allure 是一个轻量级,灵活的,支持多语言的测试报告工具
多平台,奢华的 report 框架
可以为 dev/qa 提供详尽的测试报告,测试步骤 log
也可以为管理层提供 high level 统计报告
Java 语言开发的,支持跑 pytest,javascript,PHP,ruby
可以继承到 jenkins
allure 安装
Windows/mac 通用安装方法
Releases · allure-framework/allure2 · GitHub
解压---> 进入 bin 目录---> 运行 allure.bat
把 bin 目录 加入 PATH 环境变量
Mac 可以使用 brew 安装
brew install allure
官网 :Allure Framework (qameta.io)
使用 allure2 生成精美报告
安装 allure-pytest
pip install allure-pytest
运行:
在测试执行期间收集结果
pytest[测试文件] -s -q --alluredir= ./alluredir =./result/ (--alluredir 这个选项用于指定存储测试结果的路径)
查看测试报告
测试完成后查看实际报告,在线看报告,会直接打开默认浏览器展示当前报告
allure serve ./result/
从结果生成报告,这是一个启动 Tomcat 的的服务,需要两个步骤:生成报告,打开报告
生成报告:allure generate ./result/5 -o ./report/ --clean (注意:覆盖路径加 --clean)
打开报告: allure open -h 127.0.0.1 -p 8883 ./report/
Allure 常用特性
场景
希望在报告中看到的功能,子功能或场景,测试步骤,包括测试附加信息
解决:
@Feature, @story, @step, @attach
步骤:
import allure
功能上加 @allure.feature("功能名称")
子功能上加 @allure.story("子功能名称")
步骤上加 @allure.step("步骤细节")
@allure.attach("具体文本信息"),需要附加的信息,可以是数据,文本,图片,视频网页
如果只是测试登录功能运行的时候可以添加限制过滤:
pytest 文件名 --allure-features "购物车功能" --allure-storws "加入购物车"
allure 特性-feature/story
注解 @allure.feature 与 @allure.store 的关系
feature 相当于一个功能,一个大的模块,将 case 分类到某个 feature 中,报告中 behaviore 中显示,相当于 testsuite
story 相当于对应这个功能或者模块下的不同场景,分支功能,属于 feature 之下的结构,报告在 features 中显示,相当于 testcase
feature 于 story 类似与父子关系
allure 特性-step
测试过程中每个步骤,一般都放在具体逻辑方法中
可以放在关键步骤中,在报告中显示
在 app,web 自动化测试当中,建议每切换到一个新的页面当做一个 step
用法:
@allure.step() 只能以装饰器的形式放在类或者方法上面
with allure.step(): 可以放在测试用例方法里面,但测试步骤的代码需要该语句包含
allure 特性-issue, testcase
关联测试用例(可以直接给测试用例地址链接)
关联 bug
执行的时候需要加个参数
--allure-link-pattern==issue:http://www.mytesttracker.com/issue{}
代码
@allure.issue('140','Pytest-flaky test retires shows like steps')
def test_with_issue_link():
pass
TEST_CASE_LINK ='https://github.com/qameta/allure-integrations/issuecomment-268313637'
@allure.testcase(TEST_CASE_LINK,'Test case title')
def test_with_testcase_link():
pass
按重要级别进行一定范围测试
场景
通常测试有 p0、冒烟测试、验证验证上线测试。按重要级别来分别执行的,比如上线要把主流程和重要模块都跑一遍
解决
通过 pytest.mark 标记
通过 allure.feature, allure.story
也可以通过 allure.severity 来附加标记
级别:Trivial : 不重要,Minor 不太重要,Normal :正常问题,Critical:严重,Blocker:阻塞
Blocker 级别:中断缺陷(客户端程序无响应,无法执行下一步操作)
Critical 级别:临界缺陷(功能点缺失)
Minor 级别:次要缺陷(界面错误与 UI 需求不符)
-Trival 级别:轻微缺陷(必输项无提示,或者提示不规范)
步骤
在方法,函数和类上面加
@allure.servity(allure.severity_level.TRIVIAL)
执行时
pytest -s -v 文件名 --allure-severities normal.critycal
前端自动化测试---截图
场景
前端自动化测试经常需要附加图片或 html,在适当的地方,适当的时机截图
解决
allure.attach(body(内容),name, attachment_type,extension);
allure.attach(’首页‘,’这是错误页的结果信息‘ ,allure.attachment_type.HTML)
在测试报告里附加图片:
allure.attach.file(source,name,attachment_type,extension):
allure.attach.file("./result/b.png",attachment_type=allure.attachment_type.PNG)