单元测试(unit test)是对程序中的最小可测试单元,在与程序的其它部分隔离的情况下,进行检查和验证。所谓的单元,通常是指面向过程编程中的一个函数,面向对象编程中的一个类,GUI 编程中的一个窗口等,总之它是一个人为规定的最小的被测功能模块。
单元测试是软件开发过程中,要进行的最低级别的测试活动。它由开发人员编写和执行,同时受益的也是开发人员自己,因为开发人员不仅要实现业务代码,还有责任保证代码能按照预期的方式运行。
单元测试具有回归性,也就是说可以随时运行已经编写好的单元测试。
包含断言
符合 Given-When-Then 格式
速度快,尽量别用 Sleep
避免随机输入:
UT 结果应该是决定性的,要么成功,要么失败;当使用随机输入值时,测试用例将变得不可控
避免无意义的重复
尽量避免断言时间
尽量避免依赖外部服务
要让测试用例在任何情况下都能成功执行,Mock 依赖的外部服务是更好的选择
测试用例之间相互隔离
每个测试用例只关注一个点
使用 fixture
避免在测试中使用逻辑
为了提高代码的可测性,应该遵循下面的原则:
关于如何重构代码中的坏味道,可以参考:https://mp.weixin.qq.com/s/oQbNiOzPK8mExaU9q8sE9Q。
在 Python 世界中,有很多单元测试工具,比如 unittest、mock、nose、coverage、pytest 等。其中 unittest 是 Python 标准库提供的单元测试工具,它是 Python 版本的 junit。
在 unittest 中,有几个重要的概念:
TestCase 一个 TestCase 就是一个测试用例。测试用例包括:测试前的环境准备 setUp、执行测试代码、测试后的环境清理 tearDown。 每个测试用例都有一系列的输入数据和预期输出:
输入数据
测试用例用到的外部数据。比如实参、成员变量、全局变量、IO 介质等。一个设计良好的测试用例,通常包含以下三类输入数据:
预期输出
输出是指测试用例对外部环境的更改,比如对引用参数、成员变量、全局变量、IO 介质的修改。它们的预期结果就是预期输出
TestSuite
TestSuite 采用了组合设计模式(Composite Pattern)。它既可以包含其它 TestSuite,也可以包含 TestCase,所以 TestSuite 本质就是将多个 TestCase 组合到一起,以便同时运行
TestRunner
用来执行 TestCase/TestSuite,它的 run(result) 方法会调用 TestCase/TestSuite 的 run(result) 方法,测试结果会被保存到 TestResult 中
TestResult
测试结果,包含成功了多少用例、失败了多少用例、失败信息是什么等信息
TestLoader
负责将 TestCase 加载到 TestSuite 中
unittest.loader.TestLoader 类有一个方法 loadTestsFromTestCase(testCaseClass),其参数 testCaseClass 是 TestCase 的子类。该方法会为 testCaseClass 中每个以 test 开头的方法创建一个测试用例,并将其添加到 TestSuite 中。因此如果在一个 TestCase 子类中定义了多个以 test 开头的方法,相当于同时定义了多个测试用例,这样做的目的是为了服用 test fixture(即 setUp() 和 tearDown())
xxxxxxxxxx
from unittest import TestCase, main
class Test(TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def testCase1(self):
self.assertTrue(True)
def testCase2(self):
self.assertFalse(False)
if __name__ == "__main__":
main()