软件开发固然重要,软件测试也必不可少。
一. Python 文档测试
【GitHub代码commits id:c5bdcc5】
1. pydoc生成文档
python 的 pydoc 模块可以非常方便地查看、生成 HTML 帮助文档。只要在函数、类、方法定义后面加
| |
| |
| """ |
| @File : 01_create_pydoc.py |
| @Time : 2019/8/16 11:36 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : None |
| """ |
| NMAE = 'PyDOC' |
| |
| class CreatePyDOC(object): |
| name = 'pydoc' |
| """ |
| 定义一个CreatePyDOC,该类包括两个变量:name、language |
| """ |
| def __int__(self, name, language): |
| """ |
| name --- 初始化文档的名称 |
| language --- 初始化文档的编写语言 |
| :param name: |
| :param language: |
| :return: |
| """ |
| self.name = name |
| self.age = language |
| |
| def print_info(self, language): |
| """ |
| 定义一个print_info方法 |
| language代表使用的语言 |
| :param language: |
| :return: |
| """ |
| print("%s doc 是用 %s 编写的" % (self.name, language)) |
| |
| |
| def create_doc(name, language, money): |
| """ |
| 定义一个打印创建doc信息的函数 |
| name --- 档的称 |
| language --- 文档语言 |
| money --- 售价 |
| :param name: |
| :param language: |
| :param money: |
| :return: |
| """ |
| print("doc的名字是 %s, 它的语言是 %s, 售价 %d"% (name, language, money)) |
1.1 pydoc 在控制台中查看文档
| (1) 模块的文档说明:就是*.py 文件顶部的注释信息,这部分信息会被提取成模块的文档说明。 |
| (2) CLASSES 部分:这部分会列出该模块所包含的全部类。 |
| (3) FUNCTIONS 部分:这部分会列出该模块所包含的全部函数。 |
| (4) DATA 部分:这部分会列出该模块所包含的全部成员变量。 |
| (5) FILE 部分:这部分会显示该模块对应的源文件。 |
| |
| python -m pydoc 01_create_pydoc |
| |
| |
| |
| $ python -m pydoc 01_create_pydoc |
| Help on module 01_create_pydoc: |
| |
| NAME |
| 01_create_pydoc |
| |
| DESCRIPTION |
| @File : 01_create_pydoc.py |
| @Time : 2019/8/16 11:36 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : None |
| |
| CLASSES |
| builtins.object |
| CreatePyDOC |
| |
| class CreatePyDOC(builtins.object) |
| | Methods defined here: |
| | |
| | __int__(self, name, language) |
| | name --- 初始化文档的名称 |
| | language --- 初始化文档的编写语言 |
| | :param name: |
| | :param language: |
| | :return: |
| | |
| | print_info(self, language) |
| | 定义一个print_info方法 |
| | language代表使用的语言 |
| | :param language: |
| | :return: |
| | |
| | ---------------------------------------------------------------------- |
| | Data descriptors defined here: |
| | |
| | __dict__ |
| | dictionary for instance variables (if defined) |
| | |
| | __weakref__ |
| | list of weak references to the object (if defined) |
| | |
| | ---------------------------------------------------------------------- |
| | Data and other attributes defined here: |
| | |
| | name = 'pydoc' |
| |
| FUNCTIONS |
| create_doc(name, language, money) |
| 定义一个打印创建doc信息的函数 |
| name --- 档的称 |
| language --- 文档语言 |
| money --- 售价 |
| :param name: |
| :param language: |
| :param money: |
| :return: |
| |
| DATA |
| NAME = 'PyDOC' |
| |
| FILE |
| d:\myspace\python\unittest\doc\01_create_pydoc.py |
| |
1.2 pydoc 生成 HTML 文档
| |
| python -m pydoc -w 01_create_pydoc |
| |

1.3 启动本地服务查看文档
| 第一部分显示 Python 内置的核心模块。 |
| 第二部分显示 当前目录下的所有模块。 |
| 第三部分显示 目录下的所有模块,此时在该目录下并未包含任何模块。第三部分用于显示 PYTHONPATH 环境变量所指定路径下的模块 |
| python -m pydoc -p 1234 |
| |
| |
| python -m pydoc -p 1234 |
| Server ready at http://localhost:1234/ |
| Server commands: [b]rowser, [q]uit |
| server> b --- 启动浏览器,当然也可以自己在浏览器输入地址 |
| server> q --- 退出本地服务 |
| Server stopped |
1.4 pydoc 查找模块
-k 后跟被搜索模块的部分内容,即可以进行模糊搜索
| python -m pydoc -k create |
2. doctest 文档测试
文档测试,即通过 doctest 模块运行 Python 源文件的说明文档中的测试用例,从而生成测试报告。
文档测试工具可以提取文档说明中的测试用例,">>>" 后面就表示的是测试用例,紧接着的一行则代表测试用例的输出结果(即预期结果)。文档测试工具会判断测试用例与预期结果是否一致,不一致会输出错误信息。
【GitHub代码commits id:cda157d】
| |
| |
| """ |
| @File : 02_doctest.py |
| @Time : 2019/8/17 15:48 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : None |
| """ |
| |
| import doctest |
| |
| |
| class User: |
| """ |
| 定义一个代表用户的类,该类包含两个属性: |
| name - 用户的名字 |
| age - 用户的年龄 |
| |
| >>> u = User('abc', 9) |
| >>> u.name |
| 'ABC' |
| >>> u.age |
| 9 |
| >>> u.say() |
| 'abc 说,我今年 10 岁了。' |
| """ |
| def __init__(self, name, age): |
| self.name = name |
| self.age = age |
| |
| def say(self): |
| return "%s 说,我今年 %s 岁了。" % (self.name, self.age) |
| |
| |
| if __name__ == "__main__": |
| |
| doctest.testmod() |
| |
执行以后输出的测试执行报告
| Failure |
| <Click to see difference> |
| |
| ********************************************************************** |
| File "C:/MySpace/Python/UnitTest/doc/02_doctest.py", line 23, in User |
| Failed example: |
| u.name |
| Expected: |
| 'ABC' |
| Got: |
| 'abc' |
| |
| Failure |
| <Click to see difference> |
| |
| ********************************************************************** |
| File "C:/MySpace/Python/UnitTest/doc/02_doctest.py", line 27, in User |
| Failed example: |
| u.say() |
| Expected: |
| 'abc 说,我今年 10 岁了。' |
| Got: |
| 'abc 说,我今年 9 岁了。' |
| |
| Process finished with exit code 0 |
| 第一部分:显示在哪个源文件的哪一行。 |
| 第二部分:Failed example,显示是哪个测试用例出错了。 |
| 第三部分:Expected,显示程序期望的输出结果。也就是在“>>>命令”的下一行给出的运行结果,它就是期望结果。 |
| 第四部分:Got,显示程序实际运行产生的输出结果。只有当实际运行产生的输出结果与期望结果一致时,才表明该测试用例通过。 |
二、Python 单元测试
1. PyUnit(unittest)
PyUnit(unittest)是Python自带的单元测试框架,用于编写和运行可重复的测试。PyUnit 是 xUnit 体系的成员之一,xUnit 是众多测试框架的总称。PyUnit主要用于白盒测试和回归测试。
PyUnit的特点:
| (1)让测试具有持久性,测试与开发同步进行,测试代码与开发代码一并发布; |
| (2)可以是测试代码与产品代码分离; |
| (3)针对某一个类的测试代码只需少量改动就可以应用与另一个类的测试; |
| (4)使用断言方法判断期望值与实际值的差异,返回bool值; |
| (5)测试驱动设备可以使用共同的处理化变量或实例; |
| (6)测试包结构便于组织集成运行。 |
unittest 要求单元测试类必须继承 unittest.TestCase,该类中的测试方法需要满足如下要求:
| 测试方法应该没有返回值。 |
| 测试方法不应该有任何参数。 |
| 测试方法应以test 开头。 |
当测试是不想执行某一条测试用例,除了注释测试用例相关代码外,还可以进行如下操作跳过测试用例的执行:
【GitHub代码commits id:56a251f】
| |
| |
| """ |
| @File : 03_pyunit.py |
| @Time : 2019/8/17 17:21 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : None |
| """ |
| |
| import unittest |
| |
| |
| def one_equation(a, b): |
| """ |
| 求解一元一次方程 a * x + b = 0 的解 |
| a --- 方程中变量的系数 |
| b --- 方程中的产量 |
| 返回方程的解 |
| :param a: |
| :param b: |
| :return: |
| """ |
| |
| if a == 0: |
| raise ValueError("参数错误,分母不能为0") |
| |
| else: |
| return b / a |
| |
| |
| def two_equation(a, b, c): |
| """ |
| 求解一元二次方程 a * a * x + b * x + c = 0 |
| a --- 方程中变量二次幂的系数 |
| b --- 方程中变量一次幂的系统 |
| c --- 方程中的常量 |
| :param a: |
| :param b: |
| :param c: |
| :return: |
| """ |
| if a == 0: |
| raise ValueError("参数错误") |
| elif b * b - 4 * a * c < 0: |
| raise ValueError("方程在有理数范围内无解") |
| elif b * b - 4 * a * c == 0: |
| return -b / (2 * a) |
| else: |
| r1 = (-b + (b * b - 4 * a * c) ** 0.5) / 2 / a |
| r2 = (-b - (b * b - 4 * a * c) ** 0.5) / 2 / a |
| return r1, r2 |
| |
| |
| class TestMath(unittest.TestCase): |
| |
| def test_one_equation(self): |
| self.assertEqual(one_equation(2, 5), 2.5) |
| |
| self.assertTrue(one_equation(4, -28) == -7) |
| |
| with self.assertRaises(ValueError): |
| one_equation(0, 7) |
| |
| |
| def test_two_equation(self): |
| r1, r2 = two_equation(1, -3, 2) |
| self.assertCountEqual((r1, r2), (1.0, 2.0), '求解出错') |
| r1, r2 = two_equation(2, -7, 6) |
| self.assertCountEqual((r1, r2), (1.5, 2.0), '求解出错') |
| |
| r = two_equation(1, -4, 4) |
| self.assertEqual(r, 2.0, '求解出错') |
| |
| with self.assertRaises(ValueError): |
| two_equation(0, 9, 3) |
| |
| with self.assertRaises(ValueError): |
| two_equation(4, 2, 3) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |
| |
| $ python -m unittest -v 03_pyunit.py |
| test_one_equation (03_pyunit.TestMath) ... FAIL |
| test_two_equation (03_pyunit.TestMath) ... skipped '临时跳过该测试用例不执行' |
| |
| ====================================================================== |
| FAIL: test_one_equation (03_pyunit.TestMath) |
| ---------------------------------------------------------------------- |
| Traceback (most recent call last): |
| File "C:\MySpace\Python\UnitTest\doc\03_pyunit.py", line 60, in test_one_equation |
| self.assertEqual(one_equation(2, 5), 1.5) |
| AssertionError: 2.5 != 1.5 |
| |
| ---------------------------------------------------------------------- |
| Ran 2 tests in 0.003s |
| |
| FAILED (failures=1, skipped=1) |
| |
| |
测试结果的输出结果含义:
| .:代表测试通过。 |
| F:代表测试失败,F 代表 failure。 |
| E:代表测试出错,E 代表 error。 |
| s:代表跳过该测试,s 代表 skip。 |
1.1 常用断言方法
unittest.TestCase 内置大量的 assertXXX 方法来执行断言,常用的断言方法有:
断言方法 |
检查条件 |
assertEqual(a, b) |
a == b |
assertNotEqual(a, b) |
100 |
assertTrue(x) |
bool(x) is True |
assertFalse(x) |
bool(x) is False |
assertIs(a, b) |
a is b |
assertNot(a, b) |
a is not b |
assertNone(x) |
x is None |
assertNotNone(x) |
x is not None |
assertIn(a, b) |
a in b |
assertNotIn(a, b) |
a not in b |
assertIsInstance(a ,b) |
isinstance(a, b) |
assertNotIsInstance(a, b) |
not isinstance(a, b) |
1.2 异常、日志类断言
TestCase 包含的与异常、错误、警告和日志相关的断言方法:
断言方法 |
检查条件 |
assertRaises(exc, fun, *args, **kwds) |
fun(*args, **kwds) 引发 exc 异常 |
assertRaisesRegex(exc, r, fun, *args, **kwds) |
fun(*args, **kwds) 引发 exc 异常,且异常信息匹配 r 正则表达式 |
assertWarns(warn, fun, *args, **kwds) |
fun(*args, **kwds) 引发 warn 警告 |
assertWamsRegex(warn, r, fun, *args, **kwds) |
fun(*args, **kwds) 引发 warn 警告,且警告信息匹配 r 正则表达式 |
assertLogs(logger, level) |
With 语句块使用日志器生成 level 级别的日志 |
1.3 某种特定检查断言
TestCase 包含的用于完成某种特定检查的断言方法
断言方法 |
检查条件 |
assertAlmostEqual(a, b) |
round(a-b, 7) == 0 |
assertNotAlmostEqual(a, b) |
round(a-b, 7) != 0 |
assertGreater(a, b) |
a > b |
assertGreaterEqual(a, b) |
a >= b |
assertLess(a, b) |
a < b |
assertLessEqual(a, b) |
a <= b |
assertRegex(s, r) |
r.search(s) |
assertNotRegex(s, r) |
not r.search(s) |
assertCountEqual(a, b) |
a、b 两个序列包含的元素相同,不管元素出现的顺序如何 |
1.4 针对特定类型断言
如果被判断的类型是字符串、序列、列表、元组、集合、字典,则程序会自动改为使用下表 所示的断言方法进行判断。换而言之,下表所示的断言方法其实没有必要使用,unittest 模块会自动应用它们。
断言方法 |
用于比较的类型 |
assertMultiLineEqual(a, b) |
字符串(string) |
assertSequenceEqual(a, b) |
序列(sequence) |
assertListEqual(a, b) |
列表(list) |
assertTupleEqual(a, b) |
元组(tuple) |
assertSetEqual(a, b) |
集合(set 或 frozenset) |
assertDictEqual(a, b) |
字典(dict) |
2. 测试整合
2.1 测试包(TestSuite)
测试包(TestSuite)可以组织多个测试用例,测试包还可以嵌套测试包。在使用测试包组织多个测试用例和测试包之后,程序可以使用测试运行器(TestRunner)来运行该测试用包包含的所有测试用例。
测试相关的补充:
| 测试用例类:测试用例类就是单个的测试单元,其负责检查特定输入和对应的输出是否匹配。unittest 提供了一个 TestCase 基类用于创建测试用例类。 |
| 测试包:用于组合多个测试用例,测试包也可以嵌套测试包。 |
| 测试运行器:负责组织、运行测试用例,并向用户呈现测试结果。 |
| 测试固件:代表执行一个或多个测试用例所需的准备工作,以及相关联的准备操作,准备工作可能包括创建临时数据库、创建目录、开启服务器进程等。 |
2.2 测试固件(TestFixture)
unittest.TestCase 包含了 setUp() 和 tearDown() 两个方法,其中 setUp() 方法用于初始化测试固件;而 tearDown() 方法用于销毁测试固件。程序会在运行每个测试用例(以 test_ 开头的方法)之前自动执行 setUp() 方法来初始化测试固件,井在每个测试用例(以 test_ 开头的方法)运行完成之后自动执行 tearDown() 方法来销毁测试固件。
【GitHub代码commits id:8b8e446】
| |
| |
| """ |
| @File : 04_testsuite.py |
| @Time : 2019/8/18 17:43 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : None |
| """ |
| import unittest |
| |
| |
| def say(): |
| return "Hello Python" |
| |
| |
| def func_add(a, b): |
| return a + b |
| |
| |
| def func_error(a): |
| return a |
| |
| |
| def func_skip(): |
| return "skip这个方法" |
| |
| |
| class TestSay(unittest.TestCase): |
| @classmethod |
| def setUpClass(cls) -> None: |
| print("TestSay开始测试固件...") |
| |
| def test_say(self): |
| self.assertEqual(say(), "Hello") |
| |
| @classmethod |
| def tearDownClass(cls) -> None: |
| print("TestSay结束测试固件...") |
| |
| |
| class TestAdd(unittest.TestCase): |
| @classmethod |
| def setUpClass(cls) -> None: |
| print("TestAdd开始测试固件...") |
| |
| def test_func_add(self): |
| self.assertEqual(func_add(1, 2), 3) |
| self.assertEqual(func_add(1, -2), -1) |
| self.assertEqual(func_add(-1.5, -2), -3.5) |
| |
| def test_error(self): |
| self.assertEqual(func_error(2), -2) |
| |
| @unittest.skip('这条测试用例跳过不执行') |
| def test_skip(self): |
| self.assertEqual(func_skip(), 'skip这个方法') |
| |
| @classmethod |
| def tearDownClass(cls) -> None: |
| print("TestAdd结束测试固件...") |
| |
| |
| |
| testCases = (TestSay, TestAdd) |
| |
| |
| def whole_suite(): |
| |
| loader = unittest.TestLoader() |
| |
| |
| suite = unittest.TestSuite() |
| |
| |
| for testCase in testCases: |
| |
| tests = loader.loadTestsFromTestCase(testCase) |
| |
| suite.addTests(tests) |
| |
| return suite |
| |
| |
| if __name__ == "__main__": |
| with open('04_testsuite_report.txt', 'a') as f: |
| |
| |
| runner = unittest.TextTestRunner(verbosity=2, stream=f) |
| runner.run(whole_suite()) |
| |
执行输出结果:
| $ python -m unittest -v 04_testsuite.py |
| TestAdd开始测试固件... |
| test_error (04_testsuite.TestAdd) ... FAIL |
| test_func_add (04_testsuite.TestAdd) ... ok |
| test_skip (04_testsuite.TestAdd) ... skipped '这条测试用例跳过不执行' |
| TestAdd结束测试固件... |
| TestSay开始测试固件... |
| test_say (04_testsuite.TestSay) ... FAIL |
| TestSay结束测试固件... |
| |
| ====================================================================== |
| FAIL: test_error (04_testsuite.TestAdd) |
| ---------------------------------------------------------------------- |
| Traceback (most recent call last): |
| File "C:\MySpace\Python\UnitTest\doc\04_testsuite.py", line 55, in test_error |
| self.assertEqual(func_error(2), -2) |
| AssertionError: 2 != -2 |
| |
| ====================================================================== |
| FAIL: test_say (04_testsuite.TestSay) |
| ---------------------------------------------------------------------- |
| Traceback (most recent call last): |
| File "C:\MySpace\Python\UnitTest\doc\04_testsuite.py", line 37, in test_say |
| self.assertEqual(say(), "Hello") |
| AssertionError: 'Hello Python' != 'Hello' |
| - Hello Python |
| + Hello |
| |
| |
| ---------------------------------------------------------------------- |
| Ran 4 tests in 0.004s |
| |
| FAILED (failures=2, skipped=1) |
3.批量执行用例
在大型项目测试中,我们会面临大量的测试用例,这些测试用例肯定不能一个个执行了,所以我们怎样来批量执行大量的测试用例呢?
是的,用容器。我们可以将要执行的测试用例添加到容器中,执行容器达到执行多测试用例的目的。【GitHub示例】

| |
| |
| """ |
| @File : 0203_execute_more_cases.py |
| @Time : 2019/8/28 22:00 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : 这里批量的测试用例指的是 "../testcase/ " 目录下的测试用例们 |
| """ |
| |
| import unittest |
| import os |
| |
| |
| class ExecuteMoreCases(unittest.TestCase): |
| |
| def test_execute(self): |
| |
| current_dir = os.path.dirname(os.path.abspath(__file__)) |
| print(current_dir) |
| cases_dir = os.path.join('../testcase/') |
| print(cases_dir) |
| suite = unittest.defaultTestLoader.discover(cases_dir, '00203_case1.py') |
| unittest.TextTestRunner().run(suite) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |
三、 unittest.mock
mock
是一个辅助单元测试的测试模块,在系统开发中,我们可能要依赖于外部的接口来进行开发调式,当外部的接口与当前项目平行还没稳定输出接口数据时,引入mock可以对外部依赖组件实现进行模拟并且替换掉,从而使得单元测试将焦点只放在当前的单元功能。** 本质就是个模拟数据模块。** 文章参考学习
归纳一下,我们在什么时候使用mock呢?
| 1.当依赖于第三方服务时,第三方服务出现问题但又不确定是否由于第三方服务引起的,可以通过mock来模拟第三方来排查到底是哪一方的那一块出了问题; |
| 2.同一个项目中,服务B依赖于服务A的数据,而服务A还未开发好时,可以根据事先接口的规范来mock |
| |
| |
| """ |
| @File : template_class.py |
| @Time : 2019/12/2415:56 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : None |
| """ |
| |
| class Template(object): |
| age = 11 |
| |
| def __int__(self, age): |
| self.age = age |
| self.name = "AA" |
| |
| |
| def get_full_name(self, first_name, last_name): |
| return first_name + '' + last_name |
| |
| |
| def get_age(self): |
| return self.age |
| |
| def get_name(self): |
| return self.name |
| |
| |
| @staticmethod |
| def get_class_name(): |
| return Template.__name__ |
| |
| |
| if __name__ == "__main__": |
| """ |
| 判断类是否含有某个属性:hasattr(Template, "age") |
| """ |
| |
| |
| |
| |
| |
| |
| print(Template.get_age) |
| |
1. mock成员方法
1.1 使用Mock类,返回固定值
| |
| |
| """ |
| @File : mock_member_template_value.py |
| @Time : 2019/12/2414:30 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : None |
| """ |
| import unittest |
| from unittest import mock |
| from mock.template_class import Template |
| |
| |
| class TemplateTest(unittest.TestCase): |
| |
| |
| def test_get_age(self): |
| t1 = Template() |
| |
| self.assertEqual(t1.get_age(), 11) |
| |
| t1.get_age = mock.Mock(return_value=22) |
| self.assertEqual(t1.get_age(), 22) |
| |
| |
| def test_get_full_name(self): |
| t2 = Template() |
| |
| t2.get_full_name = mock.Mock(return_value="Crisimple") |
| self.assertEqual(t2.get_full_name(), "Cris") |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |
| |
1.2 校验参数个数,再返回固定值
| |
| |
| """ |
| @File : mock_member_template_create_autospect.py |
| @Time : 2019/12/2416:00 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : 校验参数个数,再返回固定值 |
| """ |
| |
| from mock.template_class import Template |
| import unittest |
| from unittest import mock |
| |
| class TemplateTest(unittest.TestCase): |
| |
| def test_get_full_name(self): |
| t = Template() |
| |
| |
| t.get_full_name = mock.create_autospec(t.get_full_name, return_value="cris simple") |
| self.assertEqual(t.get_full_name('1', '2'), "cris simple") |
| |
| self.assertEqual(t.get_full_name('1', '2'), 'cris') |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |
| |
1.3 使用side_effect, 依次返回指定值
| |
| |
| """ |
| @File : mock_member_template_side_effect.py |
| @Time : 2019/12/2416:15 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : None |
| """ |
| |
| from mock.template_class import Template |
| import unittest |
| from unittest import mock |
| |
| |
| class TemplateTest(unittest.TestCase): |
| |
| def test_get_age(self): |
| t = Template() |
| t.get_age = mock.Mock(side_effect=[10, 11, 12]) |
| |
| self.assertEqual(t.get_age(), 10) |
| self.assertEqual(t.get_age(), 11) |
| self.assertEqual(t.get_age(), 12) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |
| |
1.4 根据参数不同,返回不同的值
| |
| |
| """ |
| @File : mock_member_template_para_value.py |
| @Time : 2019/12/2416:34 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : None |
| """ |
| |
| from mock.template_class import Template |
| from unittest import mock |
| import unittest |
| |
| |
| class TemplateTest(unittest.TestCase): |
| |
| @unittest.skip("先跳吧") |
| def test_get_full_name(self): |
| t = Template() |
| |
| values = { |
| ('cris', 'simple'): 'cris simple', |
| ('xiao', 'ming'): 'xiao ming' |
| } |
| |
| t.get_full_name = mock.Mock(side_effect=lambda x, y: values[(x, y)]) |
| self.assertEqual(t.get_full_name('cris', 'simple'), 'cris simple') |
| self.assertEqual(t.get_full_name('xiao', 'ming'), 'xiao ming') |
| |
| def test_raise_get_age(self): |
| t = Template() |
| t.get_age = mock.Mock(side_effect=TypeError("integer type")) |
| self.assertRaises(TypeError, t.get_age) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |
| |
1.5 检验是否调用
| |
| |
| """ |
| @File : mock_member_template_is_used.py |
| @Time : 2019/12/2416:53 |
| @Author : Crisimple |
| @Github : https://crisimple.github.io/ |
| @Contact : Crisimple@foxmail.com |
| @License : (C)Copyright 2017-2019, Micro-Circle |
| @Desc : None |
| """ |
| |
| from mock.template_class import Template |
| from unittest import mock |
| import unittest |
| |
| |
| class TemplateTest(unittest.TestCase): |
| |
| def test_should_validate_method_calling(self): |
| |
| t = Template() |
| |
| t.get_age = mock.Mock(return_value=22) |
| print("没有被调用过:%s" % t.get_age.assert_not_called()) |
| |
| t.get_age() |
| print("任意调用次数:%s" % t.get_age.assert_called()) |
| |
| print("只调用过一次:%s" % t.get_age.assert_called_once_with()) |
| print("只要调用过即可:%s" % t.get_age.assert_any_call()) |
| |
| |
| t.get_age.reset_mock() |
| print("没有被调用过:%s" % t.get_age.assert_not_called()) |
| |
| |
| self.assertEqual(t.get_age.called, False) |
| |
| |
| self.assertEqual(t.get_age.call_count, 0) |
| |
| t.get_age() |
| self.assertEqual(t.get_age.called, True) |
| self.assertEqual(t.get_age.call_count, 1) |
| |
| |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |
| |
2.mock静态方法
3. mock链式调用
4. mock api
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
· 分享一个我遇到过的“量子力学”级别的BUG。
· dotnet 源代码生成器分析器入门
· 官方的 MCP C# SDK:csharp-sdk
· 从零开始:基于 PyTorch 的图像分类模型
· [WPF] 在RichTextBox中输出Microsoft.Extension.Logging库的
· 一步一步教你部署ktransformers,大内存单显卡用上Deepseek-R1