Python中的单元测试框架:doctest、unittest、pytest、nose等。
单元测试:通过一段代码去验证另一段代码。
unittest是一个python版本的junit,junit是java中的单元测试框架。
1.认识unittest
1.1四个重要的概念
Test Case
Test Case是最小的测试单元,即测试用例,用于检查特定输入集合的特定返回值。unittest提供了Test Case基类,我们创建的测试类需要继承该基类,它可以用来创建新的测试用例。包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。单元测试(unit test)的本质也就在这里,一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。
Test Suite
经常被翻译成"测试套件",也有人称为"测试套",是测试用例或测试套件的集合,一般用来把需要一起执行的用例组合到一起。TestLoader是用来加载TestCase到TestSuite中的,其中有几个loadTestsFrom__()方法,就是从各个地方寻找TestCase,创建它们的实例,然后add到TestSuite中,再返回一个TestSuite实例。
Test Runner
一个组件,用于执行测试用例并向用户提供结果。unittest提供了TextTestRunner类运行测试用例,为了生成HTML格式的测试报告,后面会选择HTMLTestRunner运行类。run(test)会执行TestSuite/TestCase中的run(result)方法,测试的结果会保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。
Test Fixture
Test Fixture代表一个或多个测试所需的环境准备,以及关联的清理动作。例如:创建临时或代理把数据库、目录,或启动服务器进程。unittest中提供了setUp()/tearDown()、setUpClass()/tearDownClass()等方法来完成这些操作。
总之,test fixture的功能可以理解成是初始化和清理测试数据及环境,test case是测试用例,test suite是用例集合,test runner的作用是运行用例并返回结果。这样整个流程就清楚了,首先是要写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,整个过程集成在unittest.main模块中。
1.2断言方法
方法 | 检查点 |
---|---|
assertEqual(a, b) | a == b |
assertNotEqual(a, b) | a != b |
assertTrue(x) | bool(x) is True |
assertFalse(x) | bool(x) is False |
assertIs(a, b) | a is b |
assertIsNot(a, b) | a is not b |
assertIsNone(x) | x is None |
assertIsNotNone(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) |
assertRaises(exc, fun, *args, **kwds) | fun(*args, **kwds) raises exc |
assertRaisesRegexp(exc, r, fun, *args, **kwds) | fun(*args, **kwds) raises exc and the message matches regex r |
assertAlmostEqual(a, b) | round(a-b, 7) == 0 |
assertNotAlmostEqual(a, b) | round(a-b, 7) != 0 |
assertGreater(a, b) | a > b 2.7 |
assertGreaterEqual(a, b) | a >= b |
assertLess(a, b) | a < b |
assertLessEqual(a, b) | a <= b |
assertRegexpMatches(s, r) | r.search(s) |
assertNotRegexpMatches(s, r) | not r.search(s) |
assertItemsEqual(a, b) | sorted(a) == sorted(b) 也支持unhashable对象 |
assertDictContainsSubset(a, b) | a里面所有的键值对都在b中存在 |
举例
①创建一个被测试文件 calculator.py
#计算器类
class Calculator:
"""用于完成两个数的加减乘除"""
def __init__(self,a,b):
self.a = int(a)
self.b = int(b)
#加
def add(self):
return self.a + self.b
def sub(self):
return self.a - self.b
def mul(self):
return self.a * self.b
def div(self):
return self.a / self.b
②创建测试文件test_calculator.py
import unittest
from calculator import Calculator
class TestCalculator(unittest.TestCase):
#测试用例前置动作
def setUp(self):
print("test start:")
# 测试用例后置动作
def tearDown(self):
print("test end:")
def test_add(self):
c = Calculator(3,5)
result = c.add()
self.assertEqual(result,8)
def test_sub(self):
c = Calculator(7,2)
result = c.sub()
self.assertEqual(result,5)
def test_mul(self):
c = Calculator(3,3)
result = c.mul()
self.assertEqual(result,10)
def test_div(self):
c = Calculator(6,2)
result = c.div()
self.assertEqual(result,3)
if __name__ == '__main__':
#创建测试套件
suit = unittest.TestSuite()
suit.addTest(TestCalculator("test_add"))
suit.addTest(TestCalculator("test_sub"))
suit.addTest(TestCalculator("test_mul"))
suit.addTest(TestCalculator("test_div"))
#创建测试运行器
runner = unittest.TextTestRunner()
runner.run(suit)
setUp()/tearDown()方法,用于定义测试用例的前置和后置动作,因为在当前用例用不上,所以只做了一些简单的打印。
测试用例的执行,方法①:调用Testsuite类下面的addTest()来添加用例,一条一条按顺序执行。unittest.TestSuite().addTest()的参数是这样的:unittest.TestSuite().addTest(TestFun("test_name"));其中TestFun是继承于TestCase的类,test_name是里面的测试函数。unittest、discover()默认根据ASCII码加载测试用路,因此如果想让某个测试文件先执行,可以在命名上加以控制。(test_aaa.py会先于test_bbb.py执行) 而用unittest.TestSuite().addTest()方法执行测试用例的顺序加载测试用例的顺序相同,即书写顺序。
然后,再调用TextTestRunner类下面的run()运行测试组件。
方法②如果是main()方法,这样用,unitest.main()函数用来测试 类中以 test 开头的测试用例:
import unittest
from leap_year import LeapYear
class TestLeapYear(unittest.TestCase):
def tset_2000(self):
ly = LeapYear(2000)
self.assertEqual(ly.answer(),"2000是润年")
def tset_2004(self):
ly = LeapYear(2004)
self.assertEqual(ly.answer(),"2004是润年")
def tset_2017(self):
ly = LeapYear(2017)
self.assertEqual(ly.answer(),"2017不是润年")
def tset_2100(self):
ly = LeapYear(2100)
self.assertEqual(ly.answer(),"2100不是润年")
if __name__ == '__main__':
unittest.main()
如何执行多个测试文件:unittest中的TestLoader类提供的discover()方法可以从多个文件中查找测试用例。unittest提供了可以共享的defaultTestLoader类,可以使用其子例或方法创建实例。
discover(start_dir,pattern='test*.py',top_level_dir= None)
找到指定目录下所有测试模块,并可递归查到子目录下的测试模块,只有匹配到文件名时才加载
- start_dir:要测试的模块名或测试用例目录
- pattern='test*.py':表示用例文件名的匹配原则。此处匹配以“test”开头的.py 类型的文件,* 表示任意多个字符
- top_level_dir= None 测试模块的顶层目录,如果没有顶层目录,默认为None
discover()方法会自动根据测试用例目录找到测试用例文件(test*.py),并将找到的测试用例添加到测试套件中,因此,可以直接通过run()方法执行测试套件suits。
1.3跳过测试和预期失败
在运行测试时,有时需要直接跳过某些测试用例,或者当测试用例符合某个条件时跳过测试,又或者直接将测试用例设置为失败。unittest提供了实现这些需求的装饰器。
@unittest.skip(reason):无条件地跳过装饰的测试,需要说明跳过测试的原因
@unittest.skipIf(condition,reason):如果条件为真,则跳过装饰的测试
@unittest.skipUnless(condition,reason):当条件为真时,执行装饰的测试
@unittest.expectedFailure():不管执行结果是否正确,都将测试标记为失败
import unittest
class MyTest(unittest.TestCase):
@unittest.skip("直接跳过测试")
def test_skip(self):
print("test aaa")
@unittest.skipIf(3>2,"条件为真时跳过测试")
def test_skip_if(self):
print("test bbb")
@unittest.skipUnless(3>2,"当条件为真时执行测试")
def test_skip_unless(self):
print("test ccc")
@unittest.expectedFailure
def test_expected_failure(self):
self.assertEqual(2,2)
if __name__ == '__main__':
unittest.main()
1.4Fixture
setUpModule()/tearDownModule():在整个模块的开始与结束时被执行
setUpClass()/tearDownClass():在测试类的开始与结束时被执行,这两个为类方法,需要通过@classmethod进行装饰
setUp()/tearDown():在测试用例的开始与结束时被执行
import unittest
def setUpModule():
print("test module start >>>>>>")
def tearDownModule():
print("test module end >>>>>>")
class MyTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("test class start")
@classmethod
def tearDownClass(cls):
print("test class end")
def setUp(self):
print("test case start")
def tearDown(self):
print("test case end")
def test_case1(self):
print("test case1")
def test_case2(self):
print("test case2")
if __name__ == '__main__':
unittest.main()
结果:
test module start >>>>>>
test class starttest case start
test case1
test case end
test case start
test case2
test case end
test class end
test module end >>>>>>
2.HTML测试报告
HTMLTestRunner是unittest的一个扩展,它可以生成易于使用的HTML测试报告。
HTMLTestRunner下载地址: http://tungwaiyip.info/software/HTMLTestRunner.html
因为这个模块原本给python2.0用的,我用的python是3.x,所以下载后需要做些修改: