TestCase继承于 unittest.TestCase
测试方法必须以test开头html
测试套件: 把多个测试用例集成在一块儿就是测试套件。
1:实例化测试套件 suite = unittest.TestSuite()
2: 将测试用例加入测试套件 suite.addTest(MyTest(‘test_xxx’))python
测试执行器: 用来执行测试套件
1: 实例化测试执行器: runner = unittest.TextTestRunner()
2: 执行测试套件 : runner.run(suite)数组
测试用例类中实现了前置和后置方法,则这个测试类就是一个Fixture。
例如实现了: setUp() 和 tearDownide
# 1: 导包 import unittest # 2: 准备测试类 class MyTest(unittest.TestCase): def test_1(self): print("这是:test_1") def test_2(self): print("这是:test_2") if __name__ == '__main__': # 3: 将测试用例添加到 测试套件中。 # 实例化测试套件 suite = unittest.TestSuite() # 将测试用例添加到测试套件中。 suite.addTest(MyTest('test_1')) suite.addTest(MyTest('test_2')) # 4:运行容器中的测试用例 # 实例化测试执行器 runner = unittest.TextTestRunner() runner.run(suite)
演示:函数
# 1: 导包 import unittest # 2: 准备测试类 class MyTest(unittest.TestCase): def test_1(self): print("这是:test_1") def test_2(self): print("这是:test_2") if __name__ == '__main__': # 将当前目录下的test_1.demo.py中的测试用例所有加载到测试套件。 suite = unittest.defaultTestLoader.discover("./", "test_1_demo.py") # 运行测试用例 runner = unittest.TextTestRunner() runner.run(suite)
""" unittest的基本使用""" # 1: 导入unittest包 import unittest # 2: 建立测试用例类 class MyTest(unittest.TestCase): @classmethod def setUpClass(cls): print("setUpClass") @classmethod def tearDownClass(cls): print("tearDownClass") def setUp(self): print("setup") def tearDown(self): print("tearDown") def test_s(self): print("test_s") a = 1 + 1 self.assertEqual(a, 2, "结果不为2") def test_b(self): print("test_b") b = 1 + 2 self.assertEqual(b, 2, "结果不为2") if __name__ == '__main__': unittest.main()
运行结果:单元测试
问题: 若是一个测试用例须要传递参数,怎么传递呢??
1: 安装包:测试
pip install parameterized -i https://pypi.tuna.tsinghua.edu.cn/simple
2: 案例:ui
# 1:导包 import unittest from parameterized import parameterized # 2: 建立测试类 class MyTest(unittest.TestCase): @parameterized.expand([('renshanwen', 23), ('niuniu', 25)]) def test_param(self, name, age): print("name: %s, age: %s" % (name, age)) if __name__ == '__main__': unittest.main()
3: 运行效果:spa
Mock是Python中一个用于支持单元测试的库,它的主要功能是使用mock对象替代掉指定的Python对象,以达到模拟对象的行为。code
案例: 业务需求: 因为一段代码尚未实现,可是此时须要返回一个250的数字,此时须要使用mock代替未实现的代码,返回一个250的数字,目的是让测试流程跑通。
# 1:导包 import unittest import unittest.mock # 2: 建立模拟的类 class MyTest(unittest.TestCase): def test_return(self): # 建立一个能返回250的可调用对象 mock_obj = unittest.mock.Mock(return_value=250) # 调用mocke对象,拿到返回值 ret = mock_obj() print(ret) if __name__ == '__main__': unittest.main()
1: 利用side_effect传递一个异常:
# 1:导包 import unittest import unittest.mock # 2: 建立模拟的类 class MyTest(unittest.TestCase): def test_return(self): # 建立一个能返回250的可调用对象 mock_obj = unittest.mock.Mock(side_effect=BaseException("自定义异常")) # 调用mocke对象,拿到返回值 ret = mock_obj() print(ret) if __name__ == '__main__': unittest.main()
2:使用side_effect传递一个数组:
import unittest.mock # 2: 建立模拟的类 class MyTest(unittest.TestCase): def test_return(self): # 建立一个能返回250的可调用对象 mock_obj = unittest.mock.Mock(side_effect=[1, 2, 3]) # 调用mocke对象,拿到返回值 for i in range(3): print(mock_obj()) if __name__ == '__main__': unittest.main()
3: 使用side_effect传递一个函数:
# 1:导包 import unittest import unittest.mock # 2: 建立模拟的类 class MyTest(unittest.TestCase): def test_return(self): def sum_ab(a, b): return a + b # 建立一个能返回250的可调用对象 mock_obj = unittest.mock.Mock(side_effect=sum_ab) # 调用mocke对象,拿到返回值 print(mock_obj(1, 2)) if __name__ == '__main__': unittest.main()
业务需求: 如今有一个需求: A负责开发支付功能,可是A尚未开发完成。B负责支付状态模块,已经完成,B代码中调用A写的代码的支付函数了。如今要求C测试B写的代码是否符合要求,C如何测试??
答: 使用mock替换A写的函数,而后调用B写的函数的时候就会调用咱们的mock,不会调用A未完成的代码。
import unittest import unittest.mock import pay import pay_status class TestPay(unittest.TestCase): def test_success(self): # 用一个mock来代替pay中的pay_way函数 pay.pay_way = unittest.mock.Mock(return_value={"result": "success", "reason":"null"}) # 盗用要测试的函数 ret = pay_status.pay_way_status() self.assertEqual(ret, '支付成功', '支付失败') def test_fail(self): pay.pay_way = unittest.mock.Mock(return_value={"result": "fail", "reason":"余额不足"}) ret = pay_status.pay_way_status() # 调用支付状态函数 self.assertEqual(ret, '支付失败', '测试失败') if __name__ == '__main__': unittest.main()
提示: 当咱们使用 mock 掉一个对象后,默认状况下,在后面执行的代码都会受到影响,好比其余的测试用例,其余的代码,只要是这些代码是在 mock 以后执行的都会受影响。
import unittest import unittest.mock import pay import pay_status class TestPay(unittest.TestCase): def test_success(self): # 用一个mock来代替pay中的pay_way函数 pay.pay_way = unittest.mock.Mock(return_value={"result": "success", "reason":"null"}) # 盗用要测试的函数 ret = pay_status.pay_way_status() self.assertEqual(ret, '支付成功', '支付失败') def test_success2(self): # 盗用要测试的函数 ret = pay_status.pay_way_status() self.assertEqual(ret, '支付成功', '支付失败') if __name__ == '__main__': unittest.main()
对于上面的代码,test_success2,咱们并无使用mock来代替,结果也使用了mock。缘由就是mock一旦被使用,后面执行的测试用例只能使用mock,不能使用原来的函数了。
1: patch装饰器:
1: 方法上面加上装饰器: @mock.patch(‘被代替的函数路径’)
2: 函数另外增长一个传入值。
3: 函数内部直接使用 传入值的名字.return_value = {返回值键值对}
import unittest from unittest import mock import pay import pay_status class TestPay(unittest.TestCase): @mock.patch('pay.pay_way') def test_success(self, mock_pay_way): # 用一个mock来代替pay中的pay_way函数 mock_pay_way.return_value={"result": "success", "reason":"null"} # 盗用要测试的函数 ret = pay_status.pay_way_status() self.assertEqual(ret, '支付成功', '支付失败') if __name__ == '__main__': unittest.main()
2: patch上下文管理器:
1: 函数内部使用 : with mock.patch(‘被替代的函数路径’) as 别名:
2: 别名.return_value = {返回值键值对}
import unittest from unittest import mock import pay import pay_status class TestPay(unittest.TestCase): def test_success(self): with mock.patch('pay.pay_way') as mock_pay_way: # 用一个mock来代替pay中的pay_way函数 mock_pay_way.return_value={"result": "success", "reason":"null"} # 盗用要测试的函数 ret = pay_status.pay_way_status() self.assertEqual(ret, '支付成功', '支付失败') if __name__ == '__main__': unittest.main()
from unittest import mock import unittest class Pay(object): def pay_way(self): """假设这里是一个支付的功能,未开发完 支付成功返回:{"result": "success", "reason":"null"} 支付失败返回:{"result": "fail", "reason":"余额不足"} reason返回失败缘由 """ raise NotImplementedError('代码尚未实现') def pay_way_status(self): """根据支付的结果success或fail,判断跳转到对应页面 假设这里的功能已经开发完成""" # todo 此时pay_way()函数并未完成!你先假定他完成了 result = self.pay_way() print(result) if result["result"] == "success": return "支付成功" if result["result"] == "fail": return "支付失败"
方案一: 传统方式:
1: 先实例化这个类
2:对象.被替换函数 = mock.Mock(return_value = {返回的键值对})
3:对象.被测函数()
class TestPayStatues(unittest.TestCase): '''单元测试用例''' def test_success1(self): '''测试支付成功场景''' p = Pay() # 实例化对象 p.pay_way = mock.Mock(return_value = {"result": "success", "reason":"null"}) statues = p.pay_way_status() print(statues) self.assertEqual(statues, "支付成功")
方案二: 装饰器
1: 在测试函数上增长装饰器:@mock.patch.object(类, ‘被代替的方法名’)
2: 在增长一个函数传值,def test_success2(self, mock_obj):
3: 使用新传值.return_value = {返回的键值对}
4: 实例化类.被测试函数()
class TestPayStatues(unittest.TestCase): @mock.patch.object(Pay, 'pay_way') def test_success2(self, mock_obj): '''测试支付成功场景''' mock_obj.return_value = {"result": "success", "reason":"null"} # 根据支付结果测试页面跳转 statues = Pay().pay_way_status() print(statues) self.assertEqual(statues, "支付成功")
方案三:使用whith上下文
1: with mock.patch.object(类, ‘被代替的方法名’) as 新名字
2:新名字.return_value = {}
3: 实例化类.被测试函数()
class TestPayStatues(unittest.TestCase): def test_success3(self): '''测试支付成功场景''' with mock.patch.object(Pay, 'pay_way') as mock_obj: mock_obj.return_value = {"result": "success", "reason":"null"} # 根据支付结果测试页面跳转 statues = Pay().pay_way_status() print(statues) self.assertEqual(statues, "支付成功")
案例测试:
import unittest import unittest.mock class MockTest(unittest.TestCase): def test_return_value(self): mock_obj = unittest.mock.Mock(return_value=1999) result = mock_obj() print(result) # 打印 1999 print(mock_obj.called) # 是否被调用过, 返回布尔值 print(mock_obj.call_count) # 获取调用测试, 返回调用测试 if __name__ == '__main__': unittest.main()
1: 安装包:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple htmltestrunner-python3
2:核心代码:
if __name__ == '__main__': # 1. 把测试用例添加到suite容器中 suite = unittest.defaultTestLoader.discover('./', 'test_1_html.py') # 2. 打开文件,是一个文件对象 with open('./HTMLTestRunner.html', 'w', encoding='utf-8') as f: # 3. HTMLTestRunner()建立一个runner对象 runner = HTMLTestRunner( stream=f, # 测试报告须要写入到的文件 verbosity=2, # 控制台输出信息的详细程度, 默认为1 title='这是报告标题', # 测试报告的标题 description='这是一个测试报告' # 测试报告的描述 ) # 4. runner把容器中测试用例运行 runner.run(suite)
1: 安装包:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple beautifulreport
2: 核心代码:
if __name__ == '__main__': # 1. 把测试用例添加到suite容器中 suite = unittest.defaultTestLoader.discover('./', 'test_2_beautiful.py') # 2. 建立runner对象,同时把suite传参进入 runner = BeautifulReport(suite) # 3. 运行,同时生成测试报告 # 参数1:生成文件的注释, 参数2:生成文件的filename, 参数3:生成report的文件存储路径 runner.report('报告描述必须有,在报告中显示为用例名称', '测试报告文件名', './')