Unittest 单元测试框架2 - 编写测试代码以及如何跳过测
组织你的测试代码
单元测试的构建单位是test cases: 独立的,包含执行条件与正确性检查的方案。在unittest
中,测试用例表示为unittest.TestCase
的实例。通过编写TestCase
的子类或使用FunctionTestCase
编写你自己的测试用例。
一个TestCase
实例的测试代码必须是完全自含的,因此它可以独立运行,或与其他任意组合任意数量的测试用例一起运行。
TestCase
最简单的子类需要实现一个测试方法(例如一个明明以test
开头的方法)以执行特定的测试代码:
import unittest
class DefaultWidgetSizeTestCase(unittest.TestCase):
def test_default_widget_size(self):
widget = Widget("The widget")
self.assertEqual(widget.size(), (50, 50))
可以看到,为了进行测试,我们使用了基类TestCase
提供的一个assert*()
方法。若测试不通过,将会引发一个带有说明信息的异常,并且unittest
会将这个测试用例标记为测试不通过。任何其他类型的异常将会被当做错误处理。
可能同时存在多个前置操作相同的测试,我们可以把测试的前置操作从测试代码中拆解出来,并实现测试前置方法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')
注解: 多个测试运行的顺序由内置字符串排序方法对测试名进行排序的结果决定. |
---|
在测试运行时,若setUp()
方法引发异常,测试框架会认为测试发生了错误,因此测试方法不会被运行。
相似的,我们提供了一个tearDown()
方法在测试方法运行后进行清理工作。
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp():
self.widget = Widget('The widget')
def tearDown(self):
self.widget.dispose()
若setUp()
成功运行,无论测试方法是否成功,都会运行tearDown()
。
这样的一个测试代码运行的环境被称为test fixture。一个新的TestCase
实例作为一个test fixture
, 用于运行各个独立的测试方法。在运行每个测试时,setUp()
, tearDown()
和__init__()
会被调用一次。
建议你根据所测试的功能,将测试用TestCase实现集合起来。unittest
为此提供了机制:test suite
, 以unittest
的类TestSuite
为代表。大部分情况下,调用unittest.main()
即可, 并且它会为你集合所有模块的测试用例并执行。
然而,如果你需要自定义你的测试套件的话,你可以参考以下方法组织你的测试:
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase('test_default_widget_size'))
suite.addTest(WidgetTestCase('tset_widget_resize'))
return suite
if __name__ == '__main__':
runner = unitest.TextTestRunner()
runner.run(suite())
您可以将测试用例和测试套件的定义与要测试的代码放在相同的模块中(例如 widget.py
), 但是将测试代码放在单独的模块中有几个优点,例如test_widget.py
:
-
测试模块可以从命令行独立运行。
-
测试代码可以更容易地从附带的代码中分离出来。
-
在没有充分理由的情况下,更改测试代码以适应它所测试的代码的诱惑就更少了。
-
测试代码的修改频率应该比它测试的代码少的多。
复用已有的测试代码
一些用户希望直接使用unittest
运行已有的测试代码,而不需要把已有的每个测试函数转化为一个TestCase
的子类。
因此, unittest
提供FunctionTestCase
类。这个TestCase
的子类可用于打包已有的测试函数,并支持设置前置与后置函数。
假定有一个测试函数:
def testSomething():
something = makeSomething()
assert something.name is not None
# ...
可以创建等价的测试用例如下, 其中前置和后置方法是可选的。
testcase = unittest.FunctionTestCase(testSomething, setUp=markSomethingDB, tearDown=deleteSomethingDB)
注解: 即使可以使用FunctionTestCase 能够被将现有的测试库转换为基于unitest的系统,也不建议使用这种方法。花时间建立适当的TestCase 子类将使将来的测试重构变得更加容易。 |
---|
在某些情况下,现有的测试可能是使用doctest
模块编写的。如果是这样,doctest
提供了一个DocTestSuite
类,可以自动生成unittest
. TestSuite
现有基于doctest
的测试的实例。
跳过测试与预计的失败
3.1 新版本功能
Unittest
支持跳过单个测试方法甚至跳过整个测试类。此外,他还支持将测试标记为预期失败
, 即可以被破坏并将失败的测试,但不应再TestResult上被视为失败。
跳过测试只是使用skip()
装饰器或其变量之一,调用TestCase.skipTest()
在setUp()
或者测试方法,或者直接跑出 SkipTest
.
跳过测试的基本用法如下:
import unittest
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)L
# Tests that work for only a certain version of the library
pass
@unittest.skipUnless(sys.platform.startwith("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 appends on the external resource
pass
在啰嗦模式下运行以上测试用例时,程序输出如下:
test_nothing (__main__.MyTestCase) ... skipped 'demonstrating skipping'
test_maybe_skipped (__main__.MyTestCase) ... skipped 'external resource not available'
test_windows_support (__main__.MyTestCase) ... skipped 'requires Windows'
----------------------------------------------------------------------
Ran 4 tests in 0.005s
OK (skipped=4)
跳过测试类的写法跟跳过测试方法的写法相似:
@unittest.skip("showing class skipping")
class MySkippedTestCase(unittest.TestCase):
def test_not_run(self):
pass
TestCase.setUp()
也可以跳过测试。可以用于所需资源不可用的情况下跳过接下来的测试。
使用expectedFailure()
装饰器表明这个测试预计失败。:
class ExpectedFailureTestCase(unittest.TestCase):
@unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 0, "broken")
通过使一个decorator在测试中调用'skip()',当它想要跳过它时,就可以很容易地滚动您自己的跳过decorator。除非传递的对象具有特定属性,否则此devorator将跳过测试:
def skipUnlessHasattr(obj, attr):
if hasattr(obj, attr):
return lambda func:func
return unittest.skip("{!r} doesn't have {!r}".format(obj, attr))
以下装饰器和异常实现了测试跳过和预期的失败:
-
@unittest.skip(reason)
跳过被此装饰器装饰的测试用例, reason为测试被跳过的原因。 -
@unittest.skipIf(condition, reason)
当condition为真时,跳过被装饰的测试。 -
@unittest.skipUnless(condition, reason)
跳过被装饰的测试,除非condition为真。 -
@unittest.expectedFailure
将测试标记为预期错误。如果测试失败或出错将被视为成功。如果测试通过,则视为失败。 -
exception unitest.SkipTest(reason)
引发此异常以跳过一个测试。 通常来说,你可以使用TestCase.skipTest()
或其中一个跳过测试的装饰器实现跳过测试的功能,而不是直接引发此异常。
被跳过的测试的setUp()
和tearDown()
不会被运行。被跳过的类和setUpClass()
和tearDownClass()
不会被运行。被跳过的模组的setUpModule()
和tearDownModule()
不会被运行。
原文来自于https://docs.python.org/3.9/library/unittest.html#
如有侵权,请联系删除