Unittest Framework 简明教程
UnitTest Framework - Overview
单元测试是一种软件测试方法,通过该方法可以测试单个单元的源代码,例如函数、方法和类,以确定它们是否适合使用。直观上,可以将单元视为应用程序中最小的可测试部分。单元测试是程序员在开发过程中创建的短代码片段。它构成了组件测试的基础。
Unit testing is a software testing method by which individual units of source code, such as functions, methods, and class are tested to determine whether they are fit for use. Intuitively, one can view a unit as the smallest testable part of an application. Unit tests are short code fragments created by programmers during the development process. It forms the basis for component testing.
单元测试可以通过以下两种方式完成:
Unit testing can be done in the following two ways −
Manual Testing |
Automated Testing |
Executing the test cases manually without any tool support is known as manual testing. Since test cases are executed by human resources so it is very time consuming and tedious. As test cases need to be executed manually so more testers are required in manual testing. It is less reliable as tests may not be performed with precision each time because of human errors. No programming can be done to write sophisticated tests which fetch hidden information. |
Taking tool support and executing the test cases by using automation tool is known as automation testing. Fast Automation runs test cases significantly faster than human resources. The investment over human resources is less as test cases are executed by using automation tool. Automation tests perform precisely same operation each time they are run and are more reliable. Testers can program sophisticated tests to bring out hidden information. |
JUnit 是 Java 编程语言的单元测试框架。JUnit 在测试驱动开发的发展中非常重要,并且是单元测试框架系列中的一个,统称为 xUnit,它起源于 JUnit。您可以 JUnit Tutorial 这里。
JUnit is a unit testing framework for the Java programming language. JUnit has been important in the development of test-driven development, and is one of a family of unit testing frameworks collectively known as xUnit that originated with JUnit. You can find out JUnit Tutorial here.
Python 单元测试框架有时称为“PyUnit”,是由 Kent Beck 和 Erich Gamma 开发的 JUnit 的 Python 语言版本。从 Python 2.1 版开始,PyUnit 构成了 Python 标准库的一部分。
The Python unit testing framework, sometimes referred to as “PyUnit,” is a Python language version of JUnit developed by Kent Beck and Erich Gamma. PyUnit forms part of the Python Standard Library as of Python version 2.1.
Python 单元测试框架支持测试自动化、测试的设置和关闭代码共享、测试聚合到集合中以及测试与报告框架的独立性。unittest 模块提供了使为一组测试轻松支持这些特性的类。
Python unit testing framework supports test automation, sharing of setup and shutdown code for tests, aggregation of tests into collections, and independence of the tests from the reporting framework. The unittest module provides classes that make it easy to support these qualities for a set of tests.
本教程为初学者准备,旨在帮助他们了解 Python 测试框架的基本功能。完成本教程后,您将发现自己在使用 Python 测试框架方面的专业知识处于中等水平,您可以从该水平提升到更高水平。
This tutorial has been prepared for the beginners to help them understand the basic functionality of Python testing framework. After completing this tutorial you will find yourself at a moderate level of expertise in using Python testing framework from where you can take yourself to the next levels.
您应该具备使用 Python 语言进行软件开发的合理专业知识。我们的 Python tutorial 是开始学习 Python 的好地方。也希望具备软件测试基础知识。
You should have reasonable expertise in software development using Python language. Our Python tutorial is a good place to start learning Python. Knowledge of basics of software testing is also desirable.
Environment Setup
用于编写测试的类位于“unittest”模块中。如果您使用的是旧版的 Python(早于 Python 2.1),则可以 http://pyunit.sourceforge.net/ 下载该模块。但是,unittest 模块现在是标准 Python 发行版的一部分;因此它不需要单独安装。
The classes needed to write tests are to be found in the 'unittest' module. If you are using older versions of Python (prior to Python 2.1), the module can be downloaded from http://pyunit.sourceforge.net/. However, unittest module is now a part of the standard Python distribution; hence it requires no separate installation.
UnitTest Framework - Framework
“unittest” 支持测试自动化、测试的设置和关闭代码共享、测试聚合到集合中以及将测试独立于报告框架。
'unittest' supports test automation, sharing of setup and shutdown code for tests, aggregation of tests into collections, and independence of the tests from the reporting framework.
unittest 模块提供了使为一组测试轻松支持这些质量的类。
The unittest module provides classes that make it easy to support these qualities for a set of tests.
为实现此目的,unittest 支持以下重要概念 −
To achieve this, unittest supports the following important concepts −
-
test fixture − This represents the preparation needed to perform one or more tests, and any associate cleanup actions. This may involve, for example, creating temporary or proxy databases, directories, or starting a server process.
-
test case − This is the smallest unit of testing. This checks for a specific response to a particular set of inputs. unittest provides a base class, TestCase, which may be used to create new test cases.
-
test suite − This is a collection of test cases, test suites, or both. This is used to aggregate tests that should be executed together. Test suites are implemented by the TestSuite class.
-
test runner − This is a component which orchestrates the execution of tests and provides the outcome to the user. The runner may use a graphical interface, a textual interface, or return a special value to indicate the results of executing the tests.
Creating a Unit Test
编写一个简单单元测试涉及以下步骤 −
The following steps are involved in writing a simple unit test −
Step 1 − 在您的程序中导入 unittest 模块。
Step 1 − Import the unittest module in your program.
Step 2 − 定义要测试的函数。在以下示例中,add() 函数将受测试。
Step 2 − Define a function to be tested. In the following example, add() function is to be subjected to test.
Step 3 − 通过继承 unittest.TestCase 创建一个测试用例。
Step 3 − Create a testcase by subclassing unittest.TestCase.
Step 4 − 在类中将一个测试定义为方法。方法的名称必须以 “test” 开头。
Step 4 − Define a test as a method inside the class. Name of method must start with 'test'.
Step 5 − 每个测试都会调用 TestCase 类的 assert 函数。有许多类型的断言。以下示例调用 assertEquals() 函数。
Step 5 − Each test calls assert function of TestCase class. There are many types of asserts. Following example calls assertEquals() function.
Step 6 − assertEquals() 函数将 add() 函数的结果与 arg2 参数进行比较,并且如果比较失败,则会引发 assertionError。
Step 6 − assertEquals() function compares result of add() function with arg2 argument and throws assertionError if comparison fails.
Step 7 − 最后,从 unittest 模块调用 main() 方法。
Step 7 − Finally, call main() method from the unittest module.
import unittest
def add(x,y):
return x + y
class SimpleTest(unittest.TestCase):
def testadd1(self):
self.assertEquals(add(4,5),9)
if __name__ == '__main__':
unittest.main()
Step 8 − 从命令行运行上述脚本。
Step 8 − Run the above script from the command line.
C:\Python27>python SimpleTest.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Step 9 − 测试有以下三种可能的结果 −
Step 9 − The following three could be the possible outcomes of a test −
Sr.No |
Message & Description |
1 |
OK The test passes. ‘A’ is displayed on console. |
2 |
FAIL The test does not pass, and raises an AssertionError exception. ‘F’ is displayed on console. |
3 |
ERROR The test raises an exception other than AssertionError. ‘E’ is displayed on console. |
这些结果分别在控制台上显示为“.”、“F”和“E”。
These outcomes are displayed on the console by '.', 'F' and 'E' respectively.
Command Line Interface
可以从命令行使用 unittest 模块运行单个或多个测试。
The unittest module can be used from the command line to run single or multiple tests.
python -m unittest test1
python -m unittest test_module.TestClass
python -m unittest test_module.TestClass.test_method
unittest 支持以下命令行选项。要列出所有命令行选项,请使用以下命令
unittest supports the following command line options. For a list of all the command-line options, use the following command −
Python –m unittest -h
Sr.No |
Option & Description |
1 |
-h, --help Show this message |
2 |
v, --verbose Verbose output |
3 |
-q, --quiet Minimal output |
4 |
-f, --failfast Stop on first failure |
5 |
-c, --catch Catch control-C and display results |
6 |
-b, --buffer Buffer stdout and stderr during test runs |
UnitTest Framework - API
本章讨论 unittest 模块中定义的类和方法。此模块中有五个主要类。
This chapter discusses the classes and methods defined in the unittest module. There are five major classes in this module.
TestCase Class
此类的对象表示最小的可测试单元。它包含测试例程,并提供挂钩以便准备各个例程和清理之后的内容。
Object of this class represents the smallest testable unit. It holds the test routines and provides hooks for preparing each routine and for cleaning up thereafter.
TestCase 类中定义了以下方法 −
The following methods are defined in the TestCase class −
Sr.No |
Method & Description |
1 |
setUp() Method called to prepare the test fixture. This is called immediately before calling the test method |
2 |
tearDown() Method called immediately after the test method has been called and the result recorded. This is called even if the test method raised an exception, |
3 |
setUpClass() A class method called before tests in an individual class run. |
4 |
tearDownClass() A class method called after tests in an individual class have run. |
5 |
run(result = None) Run the test, collecting the result into the test result object passed as result. |
6 |
skipTest(reason) Calling this during a test method or setUp() skips the current test. |
7 |
debug() Run the test without collecting the result. |
8 |
shortDescription() Returns a one-line description of the test. |
Fixtures
一个 TestCase 类内可以编写很多测试。这些测试方法可能需要数据库连接、临时文件或其他资源初始化。这些称为固定装置。TestCase 包含一个特殊钩子来配置和清理你的测试所需的任何固定装置。要配置固定装置,请覆盖 setUp()。要清理,请覆盖 tearDown()。
There can be numerous tests written inside a TestCase class. These test methods may need database connection, temporary files or other resources to be initialized. These are called fixtures. TestCase includes a special hook to configure and clean up any fixtures needed by your tests. To configure the fixtures, override setUp(). To clean up, override tearDown().
在下面的示例中,在 TestCase 类内编写了两个测试。它们测试两个值相加和相减的结果。setup() 方法基于每个测试的 shortDescription() 初始化参数。teardown() 方法将在每个测试的末尾执行。
In the following example, two tests are written inside the TestCase class. They test result of addition and subtraction of two values. The setup() method initializes the arguments based on shortDescription() of each test. teardown() method will be executed at the end of each test.
import unittest
class simpleTest2(unittest.TestCase):
def setUp(self):
self.a = 10
self.b = 20
name = self.shortDescription()
if name == "Add":
self.a = 10
self.b = 20
print name, self.a, self.b
if name == "sub":
self.a = 50
self.b = 60
print name, self.a, self.b
def tearDown(self):
print '\nend of test',self.shortDescription()
def testadd(self):
"""Add"""
result = self.a+self.b
self.assertTrue(result == 100)
def testsub(self):
"""sub"""
result = self.a-self.b
self.assertTrue(result == -10)
if __name__ == '__main__':
unittest.main()
从命令行运行以上代码。它给出以下输出 −
Run the above code from the command line. It gives the following output −
C:\Python27>python test2.py
Add 10 20
F
end of test Add
sub 50 60
end of test sub
.
================================================================
FAIL: testadd (__main__.simpleTest2)
Add
----------------------------------------------------------------------
Traceback (most recent call last):
File "test2.py", line 21, in testadd
self.assertTrue(result == 100)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 2 tests in 0.015s
FAILED (failures = 1)
Class Fixture
TestCase 类有一个 setUpClass() 方法,可以在 TestCase 类内个体测试执行之前覆盖执行。类似地,tearDownClass() 方法将在类中的所有测试之后执行。这两个方法都是类方法。因此,它们必须用 @classmethod 指令装饰。
TestCase class has a setUpClass() method which can be overridden to execute before the execution of individual tests inside a TestCase class. Similarly, tearDownClass() method will be executed after all test in the class. Both the methods are class methods. Hence, they must be decorated with @classmethod directive.
下面的示例演示了这些类方法的使用 −
The following example demonstrates the use of these class methods −
import unittest
class TestFixtures(unittest.TestCase):
@classmethod
def setUpClass(cls):
print 'called once before any tests in class'
@classmethod
def tearDownClass(cls):
print '\ncalled once after all tests in class'
def setUp(self):
self.a = 10
self.b = 20
name = self.shortDescription()
print '\n',name
def tearDown(self):
print '\nend of test',self.shortDescription()
def test1(self):
"""One"""
result = self.a+self.b
self.assertTrue(True)
def test2(self):
"""Two"""
result = self.a-self.b
self.assertTrue(False)
if __name__ == '__main__':
unittest.main()
TestSuite Class
Python 的测试框架提供了一种很有用的机制,根据测试用例实例测试的功能对它们进行分组。此机制由 unittest 模块中的 TestSuite 类提供。
Python’s testing framework provides a useful mechanism by which test case instances can be grouped together according to the features they test. This mechanism is made available by TestSuite class in unittest module.
创建和运行测试套件包括以下步骤。
The following steps are involved in creating and running a test suite.
Step 1 − 创建 TestSuite 类的实例。
Step 1 − Create an instance of TestSuite class.
suite = unittest.TestSuite()
Step 2 − 在套件内添加 TestCase 类中的测试。
Step 2 − Add tests inside a TestCase class in the suite.
suite.addTest(testcase class)
Step 3 − 你还可以使用 makeSuite() 方法从一个类添加测试
Step 3 − You can also use makeSuite() method to add tests from a class
suite = unittest.makeSuite(test case class)
Step 4 − 个体测试也可以添加到套件中。
Step 4 − Individual tests can also be added in the suite.
suite.addTest(testcaseclass(""testmethod")
Step 5 − 创建 TestTestRunner 类的对象。
Step 5 − Create an object of the TestTestRunner class.
runner = unittest.TextTestRunner()
Step 6 − 调用 run() 方法运行套件中的所有测试,将结果收集到测试结果对象中
Step 6 − Call the run() method to run all the tests in the suite
runner.run (suite)
TestSuite 类中定义了以下方法 −
The following methods are defined in TestSuite class −
Sr.No |
Method & Description |
1 |
addTest() Adds a test method in the test suite. |
2 |
addTests() Adds tests from multiple TestCase classes. |
3 |
run() Runs the tests associated with this suite, collecting the result into the test result object |
4 |
debug() Runs the tests associated with this suite without collecting the result. |
5 |
countTestCases() Returns the number of tests represented by this test object |
下面的示例说明了如何使用 TestSuite 类:
The following example shows how to use TestSuite class −
import unittest
class suiteTest(unittest.TestCase):
def setUp(self):
self.a = 10
self.b = 20
def testadd(self):
"""Add"""
result = self.a+self.b
self.assertTrue(result == 100)
def testsub(self):
"""sub"""
result = self.a-self.b
self.assertTrue(result == -10)
def suite():
suite = unittest.TestSuite()
## suite.addTest (simpleTest3("testadd"))
## suite.addTest (simpleTest3("testsub"))
suite.addTest(unittest.makeSuite(simpleTest3))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
test_suite = suite()
runner.run (test_suite)
可以使用 addTest() 方法,通过取消对包含 makeSuite() 方法的行和注释语句的注释来进行实验。
You can experiment with the addTest() method by uncommenting the lines and comment statement having makeSuite() method.
TestLoader Class
unittest 包具有 TestLoader 类,用于根据类和模块创建测试套件。默认情况下,在调用 unittest.main(0 方法时自动创建 unittest.defaultTestLoader 实例。但是,显式实例可以自定义某些属性。
The unittest package has the TestLoader class which is used to create test suites from classes and modules. By default, the unittest.defaultTestLoader instance is automatically created when the unittest.main(0 method is called. An explicit instance, however enables the customization of certain properties.
在以下代码中,使用 TestLoader 对象将来自两个类的测试收集到一个列表中。
In the following code, tests from two classes are collected in a List by using the TestLoader object.
import unittest
testList = [Test1, Test2]
testLoad = unittest.TestLoader()
TestList = []
for testCase in testList:
testSuite = testLoad.loadTestsFromTestCase(testCase)
TestList.append(testSuite)
newSuite = unittest.TestSuite(TestList)
runner = unittest.TextTestRunner()
runner.run(newSuite)
下表列出 TestLoader 类中的方法列表:
The following table shows a list of methods in the TestLoader class −
S.No |
Method & Description |
1 |
loadTestsFromTestCase() Return a suite of all tests cases contained in a TestCase class |
2 |
loadTestsFromModule() Return a suite of all tests cases contained in the given module. |
3 |
loadTestsFromName() Return a suite of all tests cases given a string specifier. |
4 |
discover() Find all the test modules by recursing into subdirectories from the specified start directory, and return a TestSuite object |
TestResult Class
此类用于编译有关已成功测试和已失败测试的信息。TestResult 对象存储一组测试的结果。TestResult 实例由 TestRunner.run() 方法返回。
This class is used to compile information about the tests that have been successful and the tests that have met failure. A TestResult object stores the results of a set of tests. A TestResult instance is returned by the TestRunner.run() method.
TestResult 实例具有以下属性:
TestResult instances have the following attributes −
Sr.No |
Attribute & Description |
1 |
Errors A list containing 2-tuples of TestCase instances and strings holding formatted tracebacks. Each tuple represents a test which raised an unexpected exception. |
2 |
Failures A list containing 2-tuples of TestCase instances and strings holding formatted tracebacks. Each tuple represents a test where a failure was explicitly signalled using the TestCase.assert*() methods. |
3 |
Skipped A list containing 2-tuples of TestCase instances and strings holding the reason for skipping the test. |
4 |
wasSuccessful() Return True if all tests run so far have passed, otherwise returns False. |
5 |
stop() This method can be called to signal that the set of tests being run should be aborted. |
6 |
startTestRun() Called once before any tests are executed. |
7 |
stopTestRun() Called once after all tests are executed. |
8 |
testsRun The total number of tests run so far. |
9 |
Buffer If set to true, sys.stdout and sys.stderr will be buffered in between startTest() and stopTest() being called. |
以下代码执行了一个测试套件 −
The following code executes a test suite −
if __name__ == '__main__':
runner = unittest.TextTestRunner()
test_suite = suite()
result = runner.run (test_suite)
print "---- START OF TEST RESULTS"
print result
print "result::errors"
print result.errors
print "result::failures"
print result.failures
print "result::skipped"
print result.skipped
print "result::successful"
print result.wasSuccessful()
print "result::test-run"
print result.testsRun
print "---- END OF TEST RESULTS"
代码执行后显示以下输出 −
The code when executed displays the following output −
---- START OF TEST RESULTS
<unittest.runner.TextTestResult run = 2 errors = 0 failures = 1>
result::errors
[]
result::failures
[(<__main__.suiteTest testMethod = testadd>, 'Traceback (most recent call last):\n
File "test3.py", line 10, in testadd\n
self.assertTrue(result == 100)\nAssert
ionError: False is not true\n')]
result::skipped
[]
result::successful
False
result::test-run
2
---- END OF TEST RESULTS
UnitTest Framework - Assertion
Python 测试框架使用 Python 的内置 assert() 函数,该函数测试特定条件。如果断言失败,则会引发 AssertionError。然后,测试框架会将测试识别为失败。其他异常被视为错误。
Python testing framework uses Python’s built-in assert() function which tests a particular condition. If the assertion fails, an AssertionError will be raised. The testing framework will then identify the test as Failure. Other exceptions are treated as Error.
在 unittest 模块中定义了以下三组断言函数:
The following three sets of assertion functions are defined in unittest module −
-
Basic Boolean Asserts
-
Comparative Asserts
-
Asserts for Collections
基本断言函数评估运算结果是 True 还是 False。所有 assert 方法都接受一个 msg 参数,如果指定,则该参数在失败时用作错误消息。
Basic assert functions evaluate whether the result of an operation is True or False. All the assert methods accept a msg argument that, if specified, is used as the error message on failure.
Sr.No |
Method & Description |
1 |
assertEqual(arg1, arg2, msg = None) Test that arg1 and arg2 are equal. If the values do not compare equal, the test will fail. |
2 |
assertNotEqual(arg1, arg2, msg = None) Test that arg1 and arg2 are not equal. If the values do compare equal, the test will fail. |
3 |
assertTrue(expr, msg = None) Test that expr is true. If false, test fails |
4 |
assertFalse(expr, msg = None) Test that expr is false. If true, test fails |
5 |
assertIs(arg1, arg2, msg = None) Test that arg1 and arg2 evaluate to the same object. |
6 |
assertIsNot(arg1, arg2, msg = None) Test that arg1 and arg2 don’t evaluate to the same object. |
7 |
assertIsNone(expr, msg = None) Test that expr is None. If not None, test fails |
8 |
assertIsNotNone(expr, msg = None) Test that expr is not None. If None, test fails |
9 |
assertIn(arg1, arg2, msg = None) Test that arg1 is in arg2. |
10 |
assertNotIn(arg1, arg2, msg = None) Test that arg1 is not in arg2. |
11 |
assertIsInstance(obj, cls, msg = None) Test that obj is an instance of cls |
12 |
assertNotIsInstance(obj, cls, msg = None) Test that obj is not an instance of cls |
上面的部分断言函数在下面的代码中实现 −
Some of the above assertion functions are implemented in the following code −
import unittest
class SimpleTest(unittest.TestCase):
def test1(self):
self.assertEqual(4 + 5,9)
def test2(self):
self.assertNotEqual(5 * 2,10)
def test3(self):
self.assertTrue(4 + 5 == 9,"The result is False")
def test4(self):
self.assertTrue(4 + 5 == 10,"assertion fails")
def test5(self):
self.assertIn(3,[1,2,3])
def test6(self):
self.assertNotIn(3, range(5))
if __name__ == '__main__':
unittest.main()
当运行上面的脚本时,test2、test4 和 test6 将显示失败,其他将会成功运行。
When the above script is run, test2, test4 and test6 will show failure and others run successfully.
FAIL: test2 (__main__.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python27\SimpleTest.py", line 9, in test2
self.assertNotEqual(5*2,10)
AssertionError: 10 == 10
FAIL: test4 (__main__.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python27\SimpleTest.py", line 13, in test4
self.assertTrue(4+5==10,"assertion fails")
AssertionError: assertion fails
FAIL: test6 (__main__.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python27\SimpleTest.py", line 17, in test6
self.assertNotIn(3, range(5))
AssertionError: 3 unexpectedly found in [0, 1, 2, 3, 4]
----------------------------------------------------------------------
Ran 6 tests in 0.001s
FAILED (failures = 3)
第二组断言函数如下: comparative asserts −
The second set of assertion functions are comparative asserts −
-
assertAlmostEqual (first, second, places = 7, msg = None, delta = None) Test that first and second are approximately (or not approximately) equal by computing the difference, rounding to the given number of decimal places (default 7),
-
assertNotAlmostEqual (first, second, places, msg, delta) Test that first and second are not approximately equal by computing the difference, rounding to the given number of decimal places (default 7), and comparing to zero. In both the above functions, if delta is supplied instead of places then the difference between first and second must be less or equal to (or greater than) delta. Supplying both delta and places raises a TypeError.
-
assertGreater (first, second, msg = None) Test that first is greater than second depending on the method name. If not, the test will fail.
-
assertGreaterEqual (first, second, msg = None) Test that first is greater than or equal to second depending on the method name. If not, the test will fail
-
assertLess (first, second, msg = None) Test that first is less than second depending on the method name. If not, the test will fail
-
assertLessEqual (first, second, msg = None) Test that first is less than or equal to second depending upon the method name. If not, the test will fail.
-
assertRegexpMatches (text, regexp, msg = None) Test that a regexp search matches the text. In case of failure, the error message will include the pattern and the text. regexp may be a regular expression object or a string containing a regular expression suitable for use by re.search().
-
assertNotRegexpMatches (text, regexp, msg = None) Verifies that a regexp search does not match text. Fails with an error message including the pattern and the part of text that matches. regexp may be a regular expression object or a string containing a regular expression suitable for use by re.search().
断言函数在以下示例中实现 −
The assertion functions are implemented in the following example −
import unittest
import math
import re
class SimpleTest(unittest.TestCase):
def test1(self):
self.assertAlmostEqual(22.0/7,3.14)
def test2(self):
self.assertNotAlmostEqual(10.0/3,3)
def test3(self):
self.assertGreater(math.pi,3)
def test4(self):
self.assertNotRegexpMatches("Tutorials Point (I) Private Limited","Point")
if __name__ == '__main__':
unittest.main()
上述脚本将 test1 和 test4 报告为失败。在 test1 中,22/7 的除法不在 3.14 的 7 个小数位之内。类似地,由于第二个参数与第一个参数中的文本匹配,test4 导致 AssertionError。
The above script reports test1 and test4 as Failure. In test1, the division of 22/7 is not within 7 decimal places of 3.14. Similarly, since the second argument matches with the text in first argument, test4 results in AssertionError.
=====================================================FAIL: test1 (__main__.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "asserttest.py", line 7, in test1
self.assertAlmostEqual(22.0/7,3.14)
AssertionError: 3.142857142857143 != 3.14 within 7 places
================================================================
FAIL: test4 (__main__.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "asserttest.py", line 13, in test4
self.assertNotRegexpMatches("Tutorials Point (I) Private Limited","Point")
AssertionError: Regexp matched: 'Point' matches 'Point' in 'Tutorials Point (I)
Private Limited'
----------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (failures = 2)
Assert for Collections
这组断言函数旨在与 Python 中的集合数据类型一起使用,例如 List、Tuple、Dictionary 和 Set。
This set of assert functions are meant to be used with collection data types in Python, such as List, Tuple, Dictionary and Set.
Sr.No |
Method & Description |
1 |
assertListEqual (list1, list2, msg = None) Tests that two lists are equal. If not, an error message is constructed that shows only the differences between the two. |
2 |
assertTupleEqual (tuple1, tuple2, msg = None) Tests that two tuples are equal. If not, an error message is constructed that shows only the differences between the two. |
3 |
assertSetEqual (set1, set2, msg = None) Tests that two sets are equal. If not, an error message is constructed that lists the differences between the sets. |
4 |
assertDictEqual (expected, actual, msg = None) Test that two dictionaries are equal. If not, an error message is constructed that shows the differences in the dictionaries. |
以下示例实现了上述方法 −
The following example implements the above methods −
import unittest
class SimpleTest(unittest.TestCase):
def test1(self):
self.assertListEqual([2,3,4], [1,2,3,4,5])
def test2(self):
self.assertTupleEqual((1*2,2*2,3*2), (2,4,6))
def test3(self):
self.assertDictEqual({1:11,2:22},{3:33,2:22,1:11})
if __name__ == '__main__':
unittest.main()
在上例中,test1 和 test3 会显示 AssertionError。错误消息显示了 List 和 Dictionary 对象中的差异。
In the above example, test1 and test3 show AssertionError. Error message displays the differences in List and Dictionary objects.
FAIL: test1 (__main__.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "asserttest.py", line 5, in test1
self.assertListEqual([2,3,4], [1,2,3,4,5])
AssertionError: Lists differ: [2, 3, 4] != [1, 2, 3, 4, 5]
First differing element 0:
2
1
Second list contains 2 additional elements.
First extra element 3:
4
- [2, 3, 4]
+ [1, 2, 3, 4, 5]
? +++ +++
FAIL: test3 (__main__.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "asserttest.py", line 9, in test3
self.assertDictEqual({1:11,2:22},{3:33,2:22,1:11})
AssertionError: {1: 11, 2: 22} != {1: 11, 2: 22, 3: 33}
- {1: 11, 2: 22}
+ {1: 11, 2: 22, 3: 33}
? +++++++
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures = 2)
UnitTest Framework - Test Discovery
TestLoader 类有一个 discover() 函数。Python 测试框架将此函数用于简单的测试发现。为了保持兼容性,包含测试的模块和包必须可以从顶级目录导入。
The TestLoader class has a discover() function. Python testing framework uses this for simple test discovery. In order to be compatible, modules and packages containing tests must be importable from top level directory.
以下适用于测试发现的基本命令行用法 −
The following is the basic command line usage of test discovery −
Python –m unittest discover
解释器将尝试递归地加载所有包含测试的当前目录和内部目录中的模块。其他命令行选项 −
Interpreter tries to load all modules containing test from current directory and inner directories recursively. Other command line options are −
Sr.No |
Options & Description |
1 |
-v, --verbose Verbose output |
2 |
-s, --start-directory directory Directory to start discovery (. default) |
3 |
-p, --pattern pattern Pattern to match test files (test*.py default) |
4 |
-t, --top-level-directory directory Top level directory of project (defaults to start directory) |
例如,为了发现“tests”目录中名称以“assert”开头的模块中的测试,使用了以下命令行:
For example, in order to discover the tests in modules whose names start with 'assert' in 'tests' directory, the following command line is used −
C:\python27>python –m unittest –v –s "c:\test" –p "assert*.py"
测试发现通过导入测试来加载测试。一旦测试发现从您指定的启动目录中找到了所有测试文件,它就会将路径转换为要导入的包名。
Test discovery loads tests by importing them. Once test discovery has found all the test files from the start directory you specify, it turns the paths into package names to import.
如果您将启动目录作为包名而不是路径提供给目录,则发现会假设从中导入的任何位置都是您想要的位置,因此您不会收到警告。
If you supply the start directory as a package name rather than a path to a directory then discover assumes that whichever location it imports from is the location you intended, so you will not get the warning.
UnitTest Framework - Skip Test
自 Python 2.7 以来,已添加了跳过测试的支持。可以有条件地或无条件地跳过单个测试方法或 TestCase 类。该框架允许将某个测试标记为“预期故障”。此测试将“失败”,但不会被计为 TestResult 中的失败。
Support for skipping tests has been added since Python 2.7. It is possible to skip individual test method or TestCase class, conditionally as well as unconditionally. The framework allows a certain test to be marked as an 'expected failure'. This test will 'fail' but will not be counted as failed in TestResult.
要无条件地跳过一个方法,可以使用以下 unittest.skip() 类方法:
To skip a method unconditionally, the following unittest.skip() class method can be used −
import unittest
def add(x,y):
return x+y
class SimpleTest(unittest.TestCase):
@unittest.skip("demonstrating skipping")
def testadd1(self):
self.assertEquals(add(4,5),9)
if __name__ == '__main__':
unittest.main()
由于 skip() 是一个类方法,因此它之前带有 @ 令牌。此方法接受一个参数:描述跳过原因的日志消息。
Since skip() is a class method, it is prefixed by @ token. The method takes one argument: a log message describing the reason for the skip.
当执行以上脚本时,将在控制台上显示以下结果:
When the above script is executed, the following result is displayed on console −
C:\Python27>python skiptest.py
s
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK (skipped = 1)
字符“s”表示已跳过某个测试。
The character 's' indicates that a test has been skipped.
跳过测试的替代语法是在测试函数内使用实例方法 skipTest()。
Alternate syntax for skipping test is using instance method skipTest() inside the test function.
def testadd2(self):
self.skipTest("another method for skipping")
self.assertTrue(add(4 + 5) == 10)
以下装饰器实现测试跳过和预期失败:
The following decorators implement test skipping and expected failures −
Sr.No |
Method & Description |
1 |
unittest.skip(reason) Unconditionally skip the decorated test. reason should describe why the test is being skipped. |
2 |
unittest.skipIf(condition, reason) Skip the decorated test if condition is true. |
3 |
unittest.skipUnless(condition, reason) Skip the decorated test unless condition is true. |
4 |
unittest.expectedFailure() Mark the test as an expected failure. If the test fails when run, the test is not counted as a failure. |
下面的示例演示了如何使用条件跳过和预期失败。
The following example demonstrates the use of conditional skipping and expected failure.
import unittest
class suiteTest(unittest.TestCase):
a = 50
b = 40
def testadd(self):
"""Add"""
result = self.a+self.b
self.assertEqual(result,100)
@unittest.skipIf(a>b, "Skip over this routine")
def testsub(self):
"""sub"""
result = self.a-self.b
self.assertTrue(result == -10)
@unittest.skipUnless(b == 0, "Skip over this routine")
def testdiv(self):
"""div"""
result = self.a/self.b
self.assertTrue(result == 1)
@unittest.expectedFailure
def testmul(self):
"""mul"""
result = self.a*self.b
self.assertEqual(result == 0)
if __name__ == '__main__':
unittest.main()
在上面的示例中,将跳过 testsub() 和 testdiv()。在第一个情况下,a > b 为真,而在第二个情况下,b != 0 不为真。另一方面,testmul() 已被标记为预期失败。
In the above example, testsub() and testdiv() will be skipped. In the first case a>b is true, while in the second case b == 0 is not true. On the other hand, testmul() has been marked as expected failure.
当运行上述脚本时,两个跳过的测试将显示“s”,而预期失败将显示为“x”。
When the above script is run, two skipped tests show 's' and the expected failure is shown as 'x'.
C:\Python27>python skiptest.py
Fsxs
================================================================
FAIL: testadd (__main__.suiteTest)
Add
----------------------------------------------------------------------
Traceback (most recent call last):
File "skiptest.py", line 9, in testadd
self.assertEqual(result,100)
AssertionError: 90 != 100
----------------------------------------------------------------------
Ran 4 tests in 0.000s
FAILED (failures = 1, skipped = 2, expected failures = 1)
UnitTest Framework - Exceptions Test
Python 测试框架提供以下断言方法来检查是否引发了异常。
Python testing framework provides the following assertion methods to check that exceptions are raised.
assertRaises(exception, callable, *args, **kwds)
测试当调用函数时抛出异常(第一个参数),则只要有位置或关键字参数。当抛出预期的异常时测试通过,如果抛出其他异常则报错,如果没有抛出异常则测试失败。为了捕获一组异常,可将包含异常类的元组作为异常传递。
Test that an exception (first argument) is raised when a function is called with any positional or keyword arguments. The test passes if the expected exception is raised, is an error if another exception is raised, or fails if no exception is raised. To catch any of a group of exceptions, a tuple containing the exception classes may be passed as exception.
在下面的示例中,定义一个测试函数来检查是否抛出 ZeroDivisionError。
In the example below, a test function is defined to check whether ZeroDivisionError is raised.
import unittest
def div(a,b):
return a/b
class raiseTest(unittest.TestCase):
def testraise(self):
self.assertRaises(ZeroDivisionError, div, 1,0)
if __name__ == '__main__':
unittest.main()
testraise() 函数使用 assertRaises() 函数查看当调用 div() 函数时是否会出现除零操作。上面的代码会抛出异常。但将 div() 函数的参数更改如下 −
The testraise() function uses assertRaises() function to see if division by zero occurs when div() function is called. The above code will raise an exception. But changes arguments to div() function as follows −
self.assertRaises(ZeroDivisionError, div, 1,1)
当使用这些更改运行代码时,测试失败,因为不会出现 ZeroDivisionError。
When a code is run with these changes, the test fails as ZeroDivisionError doesn’t occur.
F
================================================================
FAIL: testraise (__main__.raiseTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "raisetest.py", line 7, in testraise
self.assertRaises(ZeroDivisionError, div, 1,1)
AssertionError: ZeroDivisionError not raised
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures = 1)
assertRaisesRegexp(exception, regexp, callable, *args, **kwds)
测试 regexp 是否与抛出异常的字符串表示匹配。regexp 可以是正则表达式对象,或包含正则表达式的字符串,该正则表达式适合 re.search() 使用。
Tests that regexp matches on the string representation of the raised exception. regexp may be a regular expression object or a string containing a regular expression suitable for use by re.search().
以下示例演示如何使用 assertRaisesRegexp() −
The following example shows how assertRaisesRegexp() is used −
import unittest
import re
class raiseTest(unittest.TestCase):
def testraiseRegex(self):
self.assertRaisesRegexp(TypeError, "invalid", reg,"Point","TutorialsPoint")
if __name__ == '__main__':
unittest.main()
在这里,testraseRegex() 测试不会失败,因为第一个参数是 “Point” 可以在第二个参数字符串中找到。
Here, testraseRegex() test doesn’t fail as first argument. "Point" is found in the second argument string.
================================================================
FAIL: testraiseRegex (__main__.raiseTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:/Python27/raiseTest.py", line 11, in testraiseRegex
self.assertRaisesRegexp(TypeError, "invalid", reg,"Point","TutorialsPoint")
AssertionError: TypeError not raised
----------------------------------------------------------------------
但是,更改如下所示 −
However, the change is as shown below −
self.assertRaisesRegexp(TypeError, "invalid", reg,123,"TutorialsPoint")
会抛出 TypeError 异常。因此,会显示以下结果 −
TypeError exception will be thrown. Hence, the following result will be displayed −
================================================================
FAIL: testraiseRegex (__main__.raiseTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "raisetest.py", line 11, in testraiseRegex
self.assertRaisesRegexp(TypeError, "invalid", reg,123,"TutorialsPoint")
AssertionError: "invalid" does not match
"first argument must be string or compiled pattern"
----------------------------------------------------------------------
UnitTest Framework - Time Test
Java 单元测试框架 Junit(Pyunit 是 JUnit 的实现)有一个便捷的超时选项。如果测试花费的时间超过指定的时间,则该测试将标记为失败。
Junit, the Java unit testing framework (Pyunit is implementation of JUnit) has a handy option of timeout. If a test takes more than specified time, it will be marked as failed.
Python 的测试框架不包含任何超时支持。但是,一个名为 timeout-decorator 的第三方模块可以完成该作业。
Python’s testing framework doesn’t contain any support for time out. However, a third part module called timeout-decorator can do the job.
从以下位置下载并安装该模块 −
Download and install the module from −
-
Import timeout_decorator in the code
-
Put timeout decorator before the test
-
@timeout_decorator.timeout(10)
如果这个时间段中的测试方法超过这里提到的超时(10 分钟),将会引发 TimeOutError。例如 −
If a test method below this line takes more than the timeout mentioned (10 mins) here, a TimeOutError will be raised. For example −
import time
import timeout_decorator
class timeoutTest(unittest.TestCase):
@timeout_decorator.timeout(5)
def testtimeout(self):
print "Start"
for i in range(1,10):
time.sleep(1)
print "%d seconds have passed" % i
if __name__ == '__main__':
unittest.main()
UnitTest Framework - Unittest2
unittest2 是一个附加功能的后移植,这些功能已加入到 Python 2.7 和更高版本的 Python 测试框架中。它经过测试,可以在 Python 2.6、2.7 和 3.* 上运行。可以从 https://pypi.python.org/pypi/unittest2 下载最新版本。
unittest2 is a backport of additional features added to the Python testing framework in Python 2.7 and onwards. It is tested to run on Python 2.6, 2.7, and 3.*. Latest version can be downloaded from https://pypi.python.org/pypi/unittest2
要使用 unittest2,而不是 unittest,请用 import unittest2 替换 import unittest。
To use unittest2 instead of unittest, simply replace import unittest with import unittest2.
unittest2 中的类派生自 unittest 中相应的类,因此可以直接使用 unittest2 测试运行基础设施,无需立即将所有测试切换为使用 unittest2。如果你打算实现新功能,请将你的测试用例从 unittest2.TestCase 子类化,而不是 unittest.TestCase
Classes in unittest2 derive from the appropriate classes in unittest, so it should be possible to use the unittest2 test running infrastructure without having to switch all your tests to using unittest2 immediately. In case you intend to implement new features, subclass your testcase from unittest2.TestCase instead of unittest.TestCase
以下是 unittest2 的新功能 −
The following are the new features of unittest2 −
-
addCleanups for better resource management
-
Contains many new assert methods
-
assertRaises as context manager, with access to the exception afterwards
-
Has module level fixtures such as setUpModule and tearDownModule
-
Includes load_tests protocol for loading tests from modules or packages
-
startTestRun and stopTestRun methods on TestResult
在 Python 2.7 中,你可以使用 python -m unittest <args> 调用 unittest 命令行功能(包括测试发现)。
In Python 2.7, you invoke the unittest command line features (including test discover) with python -m unittest <args>.
而 unittest2 附带了一个脚本 unit2。
Instead, unittest2 comes with a script unit2.
unit2 discover
unit2 -v test_module
UnitTest Framework - Signal Handling
通过使用 -c/--catch 命令行选项与 catchbreak 参数,在测试运行期间可以提高对 control-C 的处理效率。启用 catch break 行为后,control-C 将允许当前正在运行的测试完成,然后测试运行将结束并报告迄今为止的所有结果。第二次 control-c 将按通常方式引发 KeyboardInterrupt。
More efficient handling of control-C during a test run is provided by The -c/--catch command-line option to unittest, along with the catchbreak parameter. With catch break behavior enabled, control-C will allow the currently running test to complete, and the test run will then end and report all the results so far. A second control-c will raise a KeyboardInterrupt in the usual way.
如果调用了 unittest 处理程序但未安装 signal.SIGINT 处理程序,则它将调用默认处理程序。这通常是替换已安装处理程序并委派给它的代码的预期行为。对于需要禁用 unittest control-c 处理的各个测试,可以使用 removeHandler() 装饰器。
If the unittest handler is called but signal.SIGINT handler isn’t installed, then it calls for the default handler. This will normally be the expected behavior by code that replaces an installed handler and delegates to it. For individual tests that need unittest control-c handling disabled, the removeHandler() decorator can be used.
以下实用函数在测试框架中启用 control-c 处理功能:
The following utility functions enable control-c handling functionality within test frameworks −
unittest.installHandler()
安装 control-c 处理程序。当接收到 signal.SIGINT 时,会调用所有已注册结果的 TestResult.stop()。
Install the control-c handler. When a signal.SIGINT is received all registered results have TestResult.stop() called.
unittest.registerResult(result)
注册 TestResult 对象以进行 control-c 处理。注册结果将存储对其的弱引用,因此不会阻止垃圾收集该结果。
Register a TestResult object for control-c handling. Registering a result stores a weak reference to it, so it doesn’t prevent the result from being garbage collected.
unittest.removeResult(result)
移除已注册的结果。移除结果后,control-c 将不再响应该结果对象调用 TestResult.stop()。
Remove a registered result. Once a result has been removed then TestResult.stop() will no longer be called on that result object in response to a control-c.
unittest.removeHandler(function = None)
在未带参数调用时,如果已安装此函数,此函数将移除 control-c 处理程序。此函数还可以用作测试装饰器,以便在执行测试时临时移除处理程序。
When called without arguments, this function removes the control-c handler if it has been installed. This function can also be used as a test decorator to temporarily remove the handler whilst the test is being executed.
GUI Test Runner
安装 unittest 模块以交互方式发现并运行测试。此实用程序,Python 脚本“inittestgui.py”,使用了 Tkinter 模块,它是 TK 图形工具包的 Python 移植。它提供了一个易于使用的 GUI,用于发现和运行测试。
The unittest module is installed to discover and run tests interactively. This utility, a Python script 'inittestgui.py' uses Tkinter module which is a Python port for TK graphics tool kit. It gives an easy to use GUI for discovery and running tests.
Python unittestgui.py
单击“发现测试”按钮。将出现一个小的对话框,您可以在其中选择要运行测试的目录和模块。
Click the 'Discover Tests' button. A small dialog box appears where you can select directory and modules from which test are to be run.
最后,点击“开始”按钮。将会从选定的路径和模块名称发现测试,结果窗格将显示结果。
Finally, click the start button. Tests will be discovered from the selected path and module names, and the result pane will display the results.
为了看到单个测试的详细信息,选择并单击结果框中的测试即可 −
In order to see the details of individual test, select and click on test in the result box −
如果在 Python 安装中找不到此实用程序,可以从项目页面 http://pyunit.sourceforge.net/ 获取。
If you do not find this utility in the Python installation, you can obtain it from the project page http://pyunit.sourceforge.net/.
类似基于 wxpython 工具包的实用程序也可在该处获得。
Similar, utility based on wxpython toolkit is also available there.
UnitTest Framework - Doctest
Python 的标准发行版包含“Doctest”模块。此模块的功能使其能够搜索看起来像交互式 Python 会话的文本片断,并执行这些会话以查看它们是否完全按照显示的那样工作。
Python' standard distribution contains 'Doctest' module. This module’s functionality makes it possible to search for pieces of text that look like interactive Python sessions, and executes these sessions to see if they work exactly as shown.
Doctest 在以下场景中非常有用 −
Doctest can be very useful in the following scenarios −
-
To check that a module’s docstrings are up-to-date by verifying that all interactive examples still work as documented.
-
To perform regression testing by verifying that interactive examples from a test file or a test object work as expected.
-
To write tutorial documentation for a package, liberally illustrated with input-output examples
在 Python 中,‘文档字符串’是一个字符串文字,它作为类,函数或模块中的第一个表达式出现。当执行该套件时,它会被忽略,但会被编译器识别并放入封闭类,函数或模块的 doc 属性中。由于它是通过自省获得的,因此它是文档对象规则的地方。
In Python, a 'docstring' is a string literal which appears as the first expression in a class, function or module. It is ignored when the suite is executed, but it is recognized by the compiler and put into the doc attribute of the enclosing class, function or module. Since it is available via introspection, it is the canonical place for documentation of the object.
在 Python 代码不同部分使用示例用法的通常做法是将其放入文档字符串中。doctest 模块允许验证这些文档字符串在代码之间断断续续的修订中是处于最新状态。
It is a usual practice to put example usage of different parts of Python code inside the docstring. The doctest module allows to verify that these docstrings are up-to-date with the intermittent revisions in code.
在下面的代码中,一个阶乘函数与示例用法穿插定义。为了验证示例用法是否正确,请在 doctest 模块中调用 testmod() 函数。
In the following code, a factorial function is defined interspersed with example usage. In order to verify if the example usage is correct, call the testmod() function in doctest module.
"""
This is the "example" module.
The example module supplies one function, factorial(). For example,
>>> factorial(5)
120
"""
def factorial(x):
"""Return the factorial of n, an exact integer >= 0.
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: x must be >= 0
"""
if not x >= 0:
raise ValueError("x must be >= 0")
f = 1
for i in range(1,x+1):
f = f*i
return f
if __name__ == "__main__":
import doctest
doctest.testmod()
输入并保存上述脚本为 FactDocTest.py,尝试从命令行执行此脚本。
Enter and save the above script as FactDocTest.py and try to execute this script from the command line.
Python FactDocTest.py
除非示例失败,否则不会显示任何输出。现在,将命令行更改为以下内容:
No output will be shown unless the example fails. Now, change the command line to the following −
Python FactDocTest.py –v
控制台现在会显示以下输出:
The console will now show the following output −
C:\Python27>python FactDocTest.py -v
Trying:
factorial(5)
Expecting:
120
ok
Trying:
factorial(-1)
Expecting:
Traceback (most recent call last):
...
ValueError: x must be >= 0
ok
2 items passed all tests:
1 tests in __main__
1 tests in __main__.factorial
2 tests in 2 items.
2 passed and 0 failed.
Test passed.
另一方面,如果 factorial() 函数的代码在文档字符串中没有给出预期结果,则会显示失败结果。例如,在上述脚本中将 f = 1 更改为 f = 2,然后再次运行 doctest。结果如下:
If, on the other hand, the code of factorial() function doesn’t give expected result in docstring, failure result will be displayed. For instance, change f = 2 in place of f = 1 in the above script and run the doctest again. The result will be as follows −
Trying:
factorial(5)
Expecting:
120
**********************************************************************
File "docfacttest.py", line 6, in __main__
Failed example:
factorial(5)
Expected:
120
Got:
240
Trying:
factorial(-1)
Expecting:
Traceback (most recent call last):
...
ValueError: x must be >= 0
ok
1 items passed all tests:
1 tests in __main__.factorial
**********************************************************************
1 items had failures:
1 of 1 in __main__
2 tests in 2 items.
1 passed and 1 failed.
***Test Failed*** 1 failures.
Doctest: Checking Examples in a Text File
doctest 的另一个简单应用是在文本文件中测试交互式示例。这可以使用 testfile() 函数完成。
Another simple application of doctest is testing interactive examples in a text file. This can be done with the testfile() function.
以下文本存储在名为 "example.txt" 的文本文件中。
The following text is stored in a text file named 'example.txt'.
Using ''factorial''
-------------------
This is an example text file in reStructuredText format. First import
''factorial'' from the ''example'' module:
>>> from example import factorial
Now use it:
>>> factorial(5)
120
文件内容被当作文档字符串处理。为了验证文本文件中的示例,请使用 doctest 模块的 testfile() 函数。
The file content is treated as docstring. In order to verify the examples in the text file, use the testfile() function of doctest module.
def factorial(x):
if not x >= 0:
raise ValueError("x must be >= 0")
f = 1
for i in range(1,x+1):
f = f*i
return f
if __name__ == "__main__":
import doctest
doctest.testfile("example.txt")
-
As with the testmod(), testfile() won’t display anything unless an example fails. If an example does fail, then the failing example(s) and the cause(s) of the failure(s) are printed to console, using the same format as testmod().
-
In most cases a copy-and-paste of an interactive console session works fine, but doctest isn’t trying to do an exact emulation of any specific Python shell.
-
Any expected output must immediately follow the final '>>> ' or '… ' line containing the code, and the expected output (if any) extends to the next '>>> ' or all-whitespace line.
-
Expected output cannot contain an all-whitespace line, since such a line is taken to signal the end of expected output. If expected output does contain a blank line, put <BLANKLINE> in your doctest example each place a blank line is expected.
UnitTest Framework - Doctest API
doctest API 围绕下列两个容器类进行,用于从 docstring 中存储交互示例 −
The doctest API revolves around the following two container classes used to store interactive examples from docstrings −
-
Example − A single Python statement, paired with its expected output.
-
DocTest − A collection of Examples, typically extracted from a single docstring or a text file.
定义了以下附加处理类,用于查找、解析并运行以及检查 doctest 示例 −
The following additional processing classes are defined to find, parse, and run, and check doctest examples −
-
DocTestFinder − Finds all docstrings in a given module, and uses a DocTestParser to create a DocTest from every docstring that contains interactive examples.
-
DocTestParser − Creates a doctest object from a string (such as an object’s docstring).
-
DocTestRunner − Executes the examples in a doctest, and uses an OutputChecker to verify their output.
-
OutputChecker − Compares the actual output from a doctest example with the expected output, and decides whether they match.
DocTestFinder Class
它是一个处理类,用于从 docstring 及其包含对象 docstring 中提取与给定对象相关的 doctest。当前可以从以下对象类型中提取 doctest — 模块、函数、类、方法、静态方法、类方法和属性。
It is a processing class used to extract the doctests that are relevant to a given object, from its docstring and the docstrings of its contained objects. Doctests can currently be extracted from the following object types — modules, functions, classes, methods, staticmethods, classmethods, and properties.
此类定义 find() 方法。它返回由对象的 docstring,或其包含对象的任何 docstring 定义的 DocTest 列表。
This class defines the find() method. It returns a list of the DocTests that are defined by the object‘s docstring, or by any of its contained objects’ docstrings.
DocTestParser Class
这是一个处理类,用于从字符串中提取交互示例,并使用它们来创建 DocTest 对象。该类定义了以下方法 -
It is a processing class used to extract interactive examples from a string, and use them to create a DocTest object. This class defines the following methods −
-
get_doctest() − Extract all doctest examples from the given string, and collect them into a DocTest object.
-
get_examples(string[, name]) − Extract all doctest examples from the given string, and return them as a list of Example objects. Line numbers are 0-based. The optional argument name is a name identifying this string, and is only used for error messages.
-
parse(string[, name]) − Divide the given string into examples and intervening text, and return them as a list of alternating Examples and strings. Line numbers for the Examples are 0-based. The optional argument name is a name identifying this string, and is only used for error messages.
DocTestRunner Class
这是一个处理类,用于执行和验证 DocTest 中的交互示例。其中定义了以下方法 -
This is a processing class used to execute and verify the interactive examples in a DocTest. The following methods are defined in it −
report_start()
报告测试运行器即将处理给定示例。此方法是为了允许 DocTestRunner 的子类自定义其输出;不应该直接调用它
Report that the test runner is about to process the given example. This method is provided to allow subclasses of DocTestRunner to customize their output; it should not be called directly
report_success()
报告给定的示例运行成功。此方法是为了允许 DocTestRunner 的子类自定义其输出;不应该直接调用它
Report that the given example ran successfully. This method is provided to allow subclasses of DocTestRunner to customize their output; it should not be called directly.
report_failure()
报告给定的示例失败。此方法是为了允许 DocTestRunner 的子类自定义其输出;不应该直接调用它
Report that the given example failed. This method is provided to allow subclasses of DocTestRunner to customize their output; it should not be called directly.
report_unexpected_exception()
报告给定的示例引发了一个意外的异常。此方法用于允许 DocTestRunner 的子类自定义其输出;不应直接调用它。
Report that the given example raised an unexpected exception. This method is provided to allow subclasses of DocTestRunner to customize their output; it should not be called directly.
run(test)
运行测试中的示例(一个 DocTest 对象),并使用 writer 函数 out 显示结果
Run the examples in test (a DocTest object), and display the results using the writer function out.
summarize([verbose])
打印此 DocTestRunner 运行的所有测试用例的摘要,并返回一个命名的元组 TestResults(failed, attempted)。可选的 verbose 参数控制摘要的详细程度。如果未指定详细程度,则使用 DocTestRunner 的详细程度。
Print a summary of all the test cases that have been run by this DocTestRunner, and return a named tuple TestResults(failed, attempted). The optional verbose argument controls how detailed the summary is. If the verbosity is not specified, then the DocTestRunner‘s verbosity is used.
OutputChecker Class
此类用于检查 doctest 示例的实际输出是否与预期输出匹配。
This class is used to check whether the actual output from a doctest example matches the expected output.
此类中定义了以下方法 -
The following methods are defined in this class −
check_output()
如果示例(got)的实际输出与预期输出(want)匹配,则返回 True 。如果这些字符串相同,则它们始终被认为匹配;但是,根据测试运行器使用的选项标记是什么,也可能使用多种非精确匹配类型。有关选项标记的详细信息,请参阅选项标记和指令部分。
Return True if the actual output from an example (got) matches with the expected output (want). These strings are always considered to match if they are identical; but depending on what option flags the test runner is using, several non-exact match types are also possible. See section Option Flags and Directives for more information about option flags.
DocTest Integration with Unittest
doctest 模块提供了两个函数,可用于从包含 doctest 的模块和文本文件中创建 unittest 测试套件。若要与 unittest 测试发现集成,请在您的测试模块中包含一个 load_tests() 函数 -
The doctest module provides two functions that can be used to create unittest test suites from modules and text files containing doctests. To integrate with unittest test discovery, include a load_tests() function in your test module −
import unittest
import doctest
import doctestexample
def load_tests(loader, tests, ignore):
tests.addTests(doctest.DocTestSuite(doctestexample))
return tests
将形成一个 unittest 和 doctest 的测试的组合 TestSuite,现在可以通过 unittest 模块的 main() 方法或 run() 方法来执行它。
A combined TestSuite of tests from unittest as well as doctest will be formed and it can now be executed by unittest module’s main() method or run() method.
以下是用于从文本文件和包含 doctest 的模块创建 unittest.TestSuite 实例的两个主要函数 -
The following are the two main functions for creating unittest.TestSuite instances from text files and modules with the doctests −
doctest.DocFileSuite()
它用于将一个或多个文本文件中的 doctest 测试转换为 unittest.TestSuite 。返回的 unittest.TestSuite 由 unittest 框架运行,并在每个文件中运行交互式示例。如果文件中任何示例失败,则合成的单元测试也将失败,并且会引发一个 failureException 异常,该异常显示包含测试的文件的名称和(有时是近似值)行号。
It is used to convert doctest tests from one or more text files to a unittest.TestSuite. The returned unittest.TestSuite is to be run by the unittest framework and runs the interactive examples in each file. If any of the examples in a file fails, then the synthesized unit test fails, and a failureException exception is raised showing the name of the file containing the test and a (sometimes approximate) line number.
doctest.DocTestSuite()
它用于将模块的 doctest 测试转换为 unittest.TestSuite 。
It is used to convert doctest tests for a module to a unittest.TestSuite.
返回的 unittest.TestSuite 由 unittest 框架运行,并且运行模块中的每个 doctest。如果任何 doctest 失败,则合成的单元测试失败,并且引发 failureException 异常,显示包含测试的文件名称和(有时近似)行号
The returned unittest.TestSuite is to be run by the unittest framework and runs each doctest in the module. If any of the doctests fail, then the synthesized unit test fails, and a failureException exception is raised showing the name of the file containing the test and a (sometimes approximate) line number
DocTestSuite() 在底层创建一个 unittest.TestSuite ,该对象由 doctest.DocTestCase 实例和 DocTestCase(unittest.TestCase 的子类)组成。
Under the covers, DocTestSuite() creates a unittest.TestSuite out of doctest.DocTestCase instances, and DocTestCase is a subclass of unittest.TestCase.
类似地,DocFileSuite() 使用 doctest.DocFileCase 实例创建一个 unittest.TestSuite,而 DocFileCase 是 DocTestCase 的子类。
Similarly, DocFileSuite() creates a unittest.TestSuite out of doctest.DocFileCase instances, and DocFileCase is a subclass of DocTestCase.
因此,两种创建 unittest.TestSuite 的方式都运行 DocTestCase 的实例。通过将选项标志传递给 doctest 函数,当您自己运行 doctest 函数时,您可以直接控制所使用的 doctest 选项。
So both ways of creating a unittest.TestSuite run instances of DocTestCase. When you run doctest functions yourself, you can control the doctest options in use directly, by passing option flags to doctest functions.
但是,如果您正在编写一个 unittest 框架,unittest 最终会控制何时以及如何运行测试。框架作者通常想要控制 doctest 报告选项(例如,由命令行选项指定),但无法通过 unittest 将选项传递给 doctest 测试运行程序。
However, if you’re writing a unittest framework, unittest ultimately controls when and how the tests get run. The framework author typically wants to control doctest reporting options (perhaps, e.g., specified by command line options), but there’s no way to pass options through unittest to doctest test runners.
UnitTest Framework - Py.test Module
2004 年,Holger Krekel 将其 std 包重命名为“py”(仅略微没那么容易混淆),因为该名称经常与随 Python 一起提供的标准库混淆。尽管此包包含多个子包,但直到现在,它几乎完全以其 py.test 框架而闻名。
It was in 2004 that Holger Krekel renamed his std package, whose name was often confused with that of the Standard Library that ships with Python, to the (only slightly less confusing) name 'py.' Though the package contains several sub-packages, it is now known almost entirely for its py.test framework.
py.test 框架为 Python 测试制定了新的标准,如今已受到许多开发者的欢迎。它为编写测试而引入的优雅且具有 Pythonic 特性的习语使得测试套件可以使用更为简洁的风格编写。
The py.test framework has set up a new standard for Python testing, and has become very popular with many developers today. The elegant and Pythonic idioms it introduced for test writing have made it possible for test suites to be written in a far more compact style.
py.test 是 Python 标准 unittest 模块的无样板代码替代方案。它作为功能完备且可扩展的测试工具,其语法简洁。创建测试套件就像编写具有几个函数的模块一样简单。
py.test is a no-boilerplate alternative to Python’s standard unittest module. Despite being a fully-featured and extensible test tool, it boasts of a simple syntax. Creating a test suite is as easy as writing a module with a couple of functions.
py.test 可以在所有 POSIX 操作系统和 Windows (XP/7/8) 上运行,并支持 Python 2.6 及以上版本。
py.test runs on all POSIX operating systems and WINDOWS (XP/7/8) with Python versions 2.6 and above.
Installation
使用以下代码即可在当前 Python 分发中加载 pytest 模块以及 py.test.exe 实用程序。可以使用这两种方式运行测试。
Use the following code to load the pytest module in the current Python distribution as well as a py.test.exe utility. Tests can be run using both.
pip install pytest
Usage
你可以使用 assert 语句为测试期望提供断言。pytest 的断言内省将智能地报告 assert 表达式的中间值,从而使你无需学习 JUnit legacy methods 的许多名称。
You can simply use the assert statement for asserting test expectations. pytest’s assert introspection will intelligently report intermediate values of the assert expression freeing you from the need to learn the many names of JUnit legacy methods.
# content of test_sample.py
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
使用以下命令行运行上述测试。运行测试后,将在控制台上显示以下结果
Use the following command line to run the above test. Once the test is run, the following result is displayed on console −
C:\Python27>scripts\py.test -v test_sample.py
============================= test session starts =====================
platform win32 -- Python 2.7.9, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- C:\Pyth
on27\python.exe
cachedir: .cache
rootdir: C:\Python27, inifile:
collected 1 items
test_sample.py::test_answer FAILED
================================== FAILURES =====================
_________________________________ test_answer _________________________________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:7: AssertionError
========================== 1 failed in 0.05 seconds ====================
还可以使用 -m 交换机和 pytest 模块从命令行运行测试。
The test can also be run from the command line by including pytest module using –m switch.
python -m pytest test_sample.py
Grouping Multiple Tests in a Class
一旦开始拥有多个测试,通常可按逻辑将这些测试分组为类和模块。让我们编写一个包含两个测试的类
Once you start to have more than a few tests it often makes sense to group tests logically, in classes and modules. Let’s write a class containing two tests −
class TestClass:
def test_one(self):
x = "this"
assert 'h' in x
def test_two(self):
x = "hello"
assert hasattr(x, 'check')
将显示以下测试结果 −
The following test result will be displayed −
C:\Python27>scripts\py.test -v test_class.py
============================= test session starts =====================
platform win32 -- Python 2.7.9, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- C:\Pyt
on27\python.exe
cachedir: .cache
rootdir: C:\Python27, inifile:
collected 2 items
test_class.py::TestClass::test_one PASSED
test_class.py::TestClass::test_two FAILED
================================== FAILURES =====================
_____________________________ TestClass.test_two ______________________________
self = <test_class.TestClass instance at 0x01309DA0>
def test_two(self):
x = "hello"
> assert hasattr(x, 'check')
E assert hasattr('hello', 'check')
test_class.py:7: AssertionError
===================== 1 failed, 1 passed in 0.06 seconds ======================
Nose Testing - Framework
鼻塞解决方案于 2005 年版本中发布,即 py.test 获得现代外观的那一年。它由 Jason Pellerin 编写以用于 py.test 早已开创的测试惯用语,但其是一个更容易安装和维护的包。
The nose project was released in 2005, the year after py.test received its modern guise. It was written by Jason Pellerin to support the same test idioms that had been pioneered by py.test, but in a package that is easier to install and maintain.
nose 模块借助 pip 实用程序可以进行安装。
The nose module can be installed with the help of pip utility
pip install nose
这将在当前 Python 发行版和 nosetest.exe 中安装 nose 模块,这意味着可以使用该实用程序和 –m 开关来运行该测试。
This will install the nose module in the current Python distribution as well as a nosetest.exe, which means the test can be run using this utility as well as using –m switch.
C:\python>nosetests –v test_sample.py
Or
C:\python>python –m nose test_sample.py
nose 当然会从 unittest.TestCase 子类中收集测试。而且可以编写简单的测试函数,以及不是 unittest.TestCase 子类的测试类。nose 还提供了一些有用的函数,以编写时序测试、针对异常的测试以及其他常见用例。
nose collects tests from unittest.TestCase subclasses, of course. We can also write simple test functions, as well as test classes that are not subclasses of unittest.TestCase. nose also supplies a number of helpful functions for writing timed tests, testing for exceptions, and other common use cases.
nose 会自动收集测试。无需手动将测试用例收集至测试套件。运行测试具有响应性,因为 nose 会在加载第一个测试模块后立即开始运行测试。
nose collects tests automatically. There’s no need to manually collect test cases into test suites. Running tests is responsive, since nose begins running tests as soon as the first test module is loaded.
与 unittest 模块一样, nose 也支持包、模块、类和测试用例级别的 fixture,因此可以尽少地进行开销较大的初始化。
As with the unittest module, nose supports fixtures at the package, module, class, and test case level, so expensive initialization can be done as infrequently as possible.
Basic Usage
我们来看一下与之前所用脚本相类似的 nosetest.py −
Let us consider nosetest.py similar to the script used earlier −
# content of nosetest.py
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
要运行上述测试,请使用以下命令行语法 −
In order to run the above test, use the following command line syntax −
C:\python>nosetests –v nosetest.py
控制台上的输出如下 −
The output displayed on console will be as follows −
nosetest.test_answer ... FAIL
================================================================
FAIL: nosetest.test_answer
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python34\lib\site-packages\nose\case.py", line 198, in runTest
self.test(*self.arg)
File "C:\Python34\nosetest.py", line 6, in test_answer
assert func(3) == 5
AssertionError
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures = 1)
nose 可通过在上述命令行中使用 with-doctest 选项与 DocTest 进行集成。
nose can be integrated with DocTest by using with-doctest option in athe bove command line.
\nosetests --with-doctest -v nosetest.py
你可以在测试脚本中使用 nose −
You may use nose in a test script −
import nose
nose.main()
如果你不想让测试脚本在成功时退出为 0,在失败时退出为 1(如 unittest.main),请改用 nose.run()−
If you do not wish the test script to exit with 0 on success and 1 on failure (like unittest.main), use nose.run() instead −
import nose
result = nose.run()
如果测试运行成功,则结果为 true;如果失败或引发未捕获的异常,则结果为 false。
The result will be true if the test run is successful, or false if it fails or raises an uncaught exception.
nose 在包、模块、类和测试级别支持 fixture(setUp 和 tearDown 方法)。与 py.test 或 unittest fixture 一样,setUp 会始终在任何测试运行之前运行(对于测试包和模块,则在测试集合之前运行);tearDown 会在 setUp 顺利完成后运行,而与测试运行状态无关。
nose supports fixtures (setup and teardown methods) at the package, module, class, and test level. As with py.test or unittest fixtures, setup always runs before any test (or collection of tests for test packages and modules); teardown runs if setup has completed successfully, regardless of the status of the test run.
Nose Testing - Tools
nose.tools 模块提供了一些您可能发现有用的测试辅助工具,包括用于限制测试执行时间和测试异常的装饰器,以及 unittets.TestCase 中的所有同类 assertX 方法。
The nose.tools module provides a number of testing aids that you may find useful, including decorators for restricting test execution time and testing for exceptions, and all of the same assertX methods found in unittest.TestCase.
-
nose.tools.ok_(expr, msg = None) − Shorthand for assert.
-
nose.tools.eq_(a, b, msg = None) − Shorthand for ‘assert a == b, “%r != %r” % (a, b)
-
nose.tools.make_decorator(func) − Wraps a test decorator so as to properly replicate metadata of the decorated function, including nose’s additional stuff (namely, setup and teardown).
-
nose.tools.raises(*exceptions) − Test must raise one of expected exceptions to pass.
-
nose.tools.timed(limit) − Test must finish within specified time limit to pass
-
nose.tools.istest(func) − Decorator to mark a function or method as a test
-
nose.tools.nottest(func) − Decorator to mark a function or method as not a test
Parameterized Testing
Python 的测试框架 unittest 没有一个简单的方法来运行参数化测试用例。换句话说,您无法轻松从外部向 unittest.TestCase 传递参数。
Python’s testing framework, unittest, doesn’t have a simple way of running parametrized test cases. In other words, you can’t easily pass arguments into a unittest.TestCase from outside.
然而,pytest 模块通过几种完全集成的途径实现测试参数传递 −
However, pytest module ports test parametrization in several well-integrated ways −
-
pytest.fixture() allows you to define parametrization at the level of fixture functions.
-
@pytest.mark.parametrize allows to define parametrization at the function or class level. It provides multiple argument/fixture sets for a particular test function or class.
-
pytest_generate_tests enables implementing your own custom dynamic parametrization scheme or extensions.
第三方模块“nose-parameterized”允许对任何 Python 测试框架进行参数化测试。可以从以下链接下载它 − https://github.com/wolever/nose-parameterized
A third party module 'nose-parameterized' allows Parameterized testing with any Python test framework. It can be downloaded from this link − https://github.com/wolever/nose-parameterized