目录[-]
单元测试B站学习网址
https://www.bilibili.com/video/BV1s5411A7aK?p=3&spm_id_from=pageDriver
目录:
pythonproject(文件夹)
test_case_common(一级子文件夹)
calculator.py(二级py文件)
test_001_api.py(二级py文件)
main.py(一级py文件)
calculator.py
# 函数功能类
def add(a, b):
return a + b
def subtract(a, b):
return a - b
test_001_api.py
# coding=utf-8
import unittest
from calculator import add, subtract
//测试类必须继承unittest.TestCase
class Test_calculator(unittest.TestCase):
def setUp(self) -> None:
return
def tearDown(self) -> None:
return
def test_add(self):
self.assertEqual(add(10, 5), 15)
def test_substract(self):
self.assertEqual(subtract(10,5),5)
main.py
# coding=utf-8
import unittest
from htmltestreport import HTMLTestReport
import time
now = time.strftime('%Y-%m-%d_%H_%M_%S_',time.localtime())
# 1.创建测试用例集
testcase = unittest.TestCase()
# 2.创建测试套件
suite = unittest.TestSuite()
# 3.创建测试加载器
loader = unittest.TestLoader()
# 4.测试套件加载测试用例集
suite.addTest(loader.discover(start_dir=r'C:\Users\Administrator\PycharmProjects\pythonProject\test_case_common',pattern='test_*.py'))
# 5.使用HTMLTestReport模块创建执行器,默认使用unittest.Testrunner()
runner = HTMLTestReport(file_path=now+'report.html',title='测试用例',description='详情')
# 6.运行执行器,suite已包含了指定文件的全部测试用例,接下来就依次执行
runner.run(suite)
简单的示例
# coding=utf-8
import unittest
class TestMyFunction(unittest.TestCase):
def test_something(self):
# 测试代码
self.assertEqual(1 + 1, 2)
def test_another_thing(self):
# 另一个测试
self.assertTrue(isinstance("hello", str))
if __name__ == '__main__':
unittest.main()
一.Unittest和pytest
PyTest是UnitTest的衍生品,可以结合selenium、requests来实现UI和接口自动化。
鄙视链:PyTest——》UnitTest——》RobotFrameWork
UnitTest环境:在python中已经内嵌了,直接使用import unittest,就可以使用
三.两个框架的差别?
1.用例编写规则:
unittest:
- testcases:测试用例,unittest 中执行测试的最小单元
- testsuites:测试套件,理解为一系列的测试用例组合在一起
- testfixtures:测试固件或夹具,用于测试环境的创建或销毁
- testloads:测试加载器,加载”测试用例“到”测试套件“
- testrunner:测试运行器
遵守规则:
- 测试文件必须先导入库,import unittest
- 测试类必须继承unittest.TestCase
- 测试方法必须以test开头
—————————————————————
pytest:它是python的第三方测试框架,基于unittest的扩展框架,遵守规则:
- 测试文件名必须以test_开头或_test结尾
- 测试类命令必须以Test开头
- 测试方法必须test开头
2.用例的前置和后置
unittest:
- setUp和tearDown 在每个用例之前或之后运行一次,打开浏览器,加载网页/关闭网页
- setUpclass和tearDownClass 在每个类运行之前或之后运行一次
- setUpModule和tearDownModule 在每个模块之前和之后执行一次
pytest(功能更多):
方法级(通俗理解,在类里面的函数就是方法):
setup_mothod/teardown_mothod 在方法之前和之后(优先级靠前)
setup/teardown
函数级:
setup_function/teardown_function 在函数之前和之后
类级:
setup_class/teardown_class
模块:
setup_module/teardown_module
还有:还可以在函数之前加装饰器@pytest.fixture()
3.断言
unittest:assertTrue,assertEqual,assertIn(三个最常用)
pytest:assert
4.报告
unittest:htmltestrunner
pytest:插件pytest-HTML、allure
5.失败重跑
unittest:没有
pytest:pytest-rerunfailures插件
6.数据驱动
unittest:ddt
pytest:@pytest.mask.parametrize装饰器
7.用例分类执行
unittest:默认执行所有,也可以通过testsuite执行部分用例,或者-k参数
pytest:@pytest.mask
四、unittest框架做了什么
1.测试发现:从多个py文件中收集、加载测试用例
2.测试执行:将测试用例按照一定的顺序和条件执行,并且生成结果
3.测试判断:通过断言去判断结果是否正确
4.测试报告:统计测试进度、通过率、生成报告
五、Unittest重要组件
1.TestCase——测试用例
2.TestSuite——测试套件
3.TestFixture——测试固件/夹具
4.TestLoader——测试加载器
5.TestRunner——测试运行
六、TestCase(测试用例)用法
PS:只有单元测试可以使用,在self右侧,点击run,则只会运行这一个方法,这就是单元测试。
最简单的函数
import unittest
class EcshopLogin(unittest.TestCase):
# 测试用例
def test01_baili(self):
print('测试')
def test02_baili(self):
print('测试222')
def test03_baili(self):
print('测试333')
七、测试结果
.:一个句号,代表通过
F:失败
E:错误
S:跳过
八、用例执行顺序
根据测试用例的名字决定,是根据ASCII码决定顺序,所以要取一个很明确的数字ID
【0-9 A-Z a-z】A=65,a=97
九、框架底层原理
def __init__(self,
module='__main__', #用例路径__main__表示当前模块的路径
defaultTest=None, #待测用例的名称,默认是空
argv=None, #接收外部参数
testRunner=None, #"测试运行器",TextTestRunner
testLoader=loader.defaultTestLoader, #指定使用默认的"测试用例加载器"
exit=True, #是否在测试完成之后结束python程序
verbosity=1, #类似命令行,代表详细的返回,如果写2,就代表简约风格;如果写0,连点都没有
failfast=None,
catchbreak=None,
buffer=None,
warnings=None,
*,
tb_locals=False):
调用代码的方式:
第一种:执行全部代码
unittest.main()
第二种:执行指定的部分用例
suite=unittest.TestSuite() suite.addTest(EcshopLogin('test01_baili')) suite.addTest(EcshopLogin('test02_baili')) unittest.TextTestRunner().run(suite)
第三种:把用例写到一个目录里
suite=unittest.TestSuite()
testcases=[EcshopLogin('test01_baili'),EcshopLogin('test02_baili')]
suite.addTests(testcases)
unittest.main(defaultTest='suite')
第四种:这个也不算麻烦,不能用os.getcwd(),这个命令拿到的是工作目录,并不一定和py所在目录一致
所以更建议使用os.path.dirname调用
suite=unittest.TestSuite()
testcases=unittest.defaultTestLoader.discover(start_dir=os.path.dirname(os.path.abspath(__file__)),pattern='*.py')
suite.addTests(testcases)
unittest.main(defaultTest='suite')
一.封装unittest类
新建一个其他文件my_unit.py,将需要封装的内容调整到这里
import unittest
class MyUnit(unittest.TestCase):
@classmethod
def setUpClass(cls):
print('setUpClass:在每个类之前执行一次,如创建数据库、生成日志对象等')
def setUp(self):
print('\nsetUp:在测试用例之前的准备工作,如打开浏览器、加载网页')
def tearDown(self):
print('teatDown:在测试用例之后的扫尾工作,如关闭浏览器')
@classmethod
def tearDownClass(cls):
print('tearDownClass:在每个类之后执行一次,如关闭数据库、销毁日志对象等')
另一个文件,作为主文件,继承它就可以使用了。
from my_unit import MyUnit
class Test(MyUnit):
def test01_baili(self):
print('测试baili')
def test02_weiwei(self):
print('测试weiwei')
二.忽略测试用例
age=25
class Test(MyUnit):
@unittest.skip('忽略')
def test01_baili(self):
print('测试baili')
@unittest.skipIf(age>=25,'大于等于25岁,为真时忽略.')
def test02_weiwei(self):
print('测试weiwei')
@unittest.skipUnless(age<25,'小于25岁,为假时忽略.')
def test03_zhizhi(self):
print('测试zhizhi')
skipUnless,为假时忽略,为真时不忽略,会打印出来。
忽略所有用例,则在类上面加装饰器
三.unittest断言:
assertEqual(a,b) 断言a==b
assertNotEqual(a,b) 断言a!=b
assertTrue(a) 断言a为真
assertFalse(a) 断言a为假
assertIn(a,b) 断言a在b里
assertNotIn(a,b) 断言a不在b里
四.生成HTTP报告
1.下载HTTPTestRunner,并放到python\Lib目录下,之后可直接使用
五.跳过测试
class TestSkip(unittest.TestCase):
@unittest.skip("跳过这个测试")
def test_skip(self):
self.fail("不应该执行")
@unittest.skipIf(condition, "条件为真时跳过")
def test_skip_if(self):
pass
@unittest.skipUnless(condition, "条件为假时跳过")
def test_skip_unless(self):
pass
@unittest.expectedFailure
def test_expected_failure(self):
self.assertEqual(1, 0) # 这个测试预期会失败
六.命令行用法
注意,执行命令行时,需要在单元测试类,也就是包含unittest.TestCase的文件下输入,否则识别不到不会执行
# 运行所有测试
python -m unittest discover
# 运行特定测试模块(文件名)
python -m unittest test_module
# 运行特定测试类(类名)
python -m unittest test_module.TestClass
# 运行特定测试方法(类里面对应的方法)
python -m unittest test_module.TestClass.test_method
# 详细输出
python -m unittest -v
# 发现并运行所有测试(推荐方式)————文件夹对应的是,包含所有测试类的文件
python -m unittest discover -s 文件夹 -p "test_*.py"
七.高级特性
# 子测试
class TestSubTest(unittest.TestCase):
def test_even(self):
"""测试0-5是否为偶数"""
for i in range(0, 6):
# 如果不用with语句,执行一次后程序停止
with self.subTest(i=i):
self.assertEqual(i % 2, 0)
# 自定义断言
class TestCustomAssert(unittest.TestCase):
def assertIsEven(self, value):
self.assertEqual(value % 2, 0, f"{value} 不是偶数")
def test_custom_assert(self):
self.assertIsEven(4)