单元测试
- unittest包括Python标准库中的测试模型。创建测试用例通过继承unittest.Testcase来实现。
#简单测试字符串的方法
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
- 一个类继承了unittest.TestCase就是创建了一个测试样例。每一个测试的关键是调用assertEqual()来检查预期的输出,调用assertTrue()或者assertFalse()来验证一个条件。调用assertRaises()来验证抛出一个特定的异常。使用这些方法而不使用assert语句是为了让测试运行者能聚合所有测试结果并产生结果报告。
- 测试用例结构。在运行每一个测试用例的时候,setUp(),tearDown(),init()会被调用一次。如果同时存在多个前置操作相同的测试,我们可以把测试的前置操作从测试代码中拆解出来,并实现测试前置方法setUp()。在运行测试用例的时候,测试框架会自动地为每个单独测试调用前置方法。
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def test_default_widget_size(self):
self.assertEqual(self.widget.size(), (50,50),
'incorrect default size')
def test_widget_resize(self):
self.widget.resize(100,150)
self.assertEqual(self.widget.size(), (100,150),
'wrong size after resize')
类似的,tearDown()方法在测试方法啊运行后进行清理工作。无论测试方法是否成功都会运行tearDown()。
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def tearDown(self):
self.widget.dispose()
- unittest.main()提供了一个测试脚本的命令行接口。unittest 模块可以通过命令行运行模块、类和独立测试方法的测试
python -m unittest test_module1 test_module2
python -m unittest test_module.TestClass
python -m unittest test_module.TestClass.test_method
- unittest提供FunctionTestCase类,可用于打包已有的测试函数,并支持设置前置函数和后置函数。
#假设有一个测试函数
def testSomething():
something = makeSomething()
assert something.name is not None
#可以创建等价的测试用例,并且前置函数和后置函数
testcase = unittest.FunctionTestCase(testSomething,
setUp=makeSomethingDB,
tearDown=deleteSomethingDB)
- 3.1版本支持跳过测试和预计测试用例失败
- @unittest.skip(reason) 跳过被这个装饰器修饰的测试用例,reason表示原因(字符串)
- @unittest.skipIf(condition, reason) 当condition为True的时候,跳过被这个装饰器修饰的测试用例
- @unittest.skipUnless(condition, reason) 当condition为False的时候,跳过被这个装饰器修饰的测试用例,也就是condition成立的时候执行这个测试用例
- @unittest.expectedFailure 把测试用例标记为预计失败,如果测试不通过,表示测试用例成功,测试通过,表示测试用例失败
#跳过的举例
class MyTestCase(unittest.TestCase):
@unittest.skip("demonstrating skipping")
def test_nothing(self):
self.fail("shouldn't happen")
@unittest.skipIf(mylib.__version__ < (1, 3),"not supported in this library version")
def test_format(self):
# Tests that work for only a certain version of the library.
pass
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_windows_support(self):
# windows specific testing code
pass
def test_maybe_skipped(self):
if not external_resource_available():
self.skipTest("external resource not available")
# test code that depends on the external resource
pass
#预计失败的举例
class ExpectedFailureTestCase(unittest.TestCase):
@unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 0, "broken")
- 3.4版本的新功能,通过subTest()来展示下标
class NumbersTest(unittest.TestCase):
def test_even(self):
"""
Test that numbers between 0 and 5 are all even.
"""
for i in range(0, 6):
with self.subTest(i=i):
self.assertEqual(i % 2, 0)
那么在展示结果的时候就是
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=3)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=5)
----------------------------------------------------------------------
Traceback (most recent call last):
File "subtests.py", line 32, in test_even
self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
可以通过i的展示来寻找问题。
- testCase提供一些断言的方法来检查并报告故障
方法 | 检查 | 版本 |
---|---|---|
assertEqual(a,b,Msg=None) | 检查a,b是否相等 | |
assertNotEqual(a,b,Msg=None) | 检查a.b.是否不相等 | |
asserTrue(x) | 检查x是否为真 | |
asserFlase(x) | 检查是否为假 | |
assertIs(a,b) | 检查a.b是否是同类型 | 3.1 |
assertIsNot(a,b) | 检查a.b是否不是同类型的 | 3.1 |
asserIsNone(x) | 检查x是否为None | 3.1 |
assertIsNotNone(x) | 检查x是否不是None | 3.1 |
assertIn(a,b) | 检查a是否在b中 | 3.1 |
assertNotIn(a,b) | 检查a是否不在b中的值 | 3.1 |
assertIsInstance(object,classinfo) | 检查如果object是classinfo的实例或者是其子类,则返回True | 3.2 |
assertNotIsInstance(object,classinfo) | 检查如果object不是classinfo的实例或者是其子类,则返回True | 3.2 |
文档测试
- python中有doctest模块查找零碎文本,就像在Python中docstrings内的交互式会话,执行那些会话以证实工作正常。
#简单的函数文档测试
def square(x):
"""返回 x 的平方。
>>> square(2)
4
>>> square(-2)
4
"""
return x + x
if __name__ == '__main__':
import doctest
doctest.testmod()
然后运行之后的命令行展示
**********************************************************************
File "test_doctest.py", line 7, in __main__.square
Failed example:
square(-2)
Expected:
4
Got:
-4
**********************************************************************
工具 py.test
#举例有一个测试文件
# content of test_sample.py
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
在这个文件目录下运行
C:\Users\Administrator\Desktop\test>py.test
================================================= test session starts =================================================
platform win32 -- Python 3.7.6, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: C:\Users\Administrator\Desktop\test
collected 1 item
test_doctest.py F [100%]
====================================================== FAILURES =======================================================
_____________________________________________________ test_answer _____________________________________________________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_doctest.py:6: AssertionError
================================================== 1 failed in 0.03s ==================================================