Unittest Framework 简明教程

UnitTest Framework - Quick Guide

UnitTest Framework - Overview

单元测试是一种软件测试方法,通过该方法可以测试单个单元的源代码,例如函数、方法和类,以确定它们是否适合使用。直观上,可以将单元视为应用程序中最小的可测试部分。单元测试是程序员在开发过程中创建的短代码片段。它构成了组件测试的基础。

单元测试可以通过以下两种方式完成:

Manual Testing

Automated Testing

在没有工具支持的情况下手动执行测试用例称为手动测试。由于测试用例是由人力资源执行的,所以它非常 time consuming and tedious 。由于测试用例需要手动执行,因此手动测试需要更多测试人员。由于人为错误,每次进行测试时测试可能无法精确执行,因此它的可靠性较低。无法编写出色的测试来获取隐藏信息。

获取工具支持并使用自动化工具执行测试用例称为自动化测试。快速自动化比人力资源更快地运行测试用例。 investment over human resources is less 因为使用自动化工具执行测试用例。自动化测试每次运行时都执行完全相同的操作,并且 are more reliable 。测试人员 can program sophisticated tests ,以找出隐藏的信息。

JUnit 是 Java 编程语言的单元测试框架。JUnit 在测试驱动开发的发展中非常重要,并且是单元测试框架系列中的一个,统称为 xUnit,它起源于 JUnit。您可以 JUnit Tutorial 这里。

Python 单元测试框架有时称为“PyUnit”,是由 Kent Beck 和 Erich Gamma 开发的 JUnit 的 Python 语言版本。从 Python 2.1 版开始,PyUnit 构成了 Python 标准库的一部分。

Python 单元测试框架支持测试自动化、测试的设置和关闭代码共享、测试聚合到集合中以及测试与报告框架的独立性。unittest 模块提供了使为一组测试轻松支持这些特性的类。

本教程为初学者准备,旨在帮助他们了解 Python 测试框架的基本功能。完成本教程后,您将发现自己在使用 Python 测试框架方面的专业知识处于中等水平,您可以从该水平提升到更高水平。

您应该具备使用 Python 语言进行软件开发的合理专业知识。我们的 Python tutorial 是开始学习 Python 的好地方。也希望具备软件测试基础知识。

Environment Setup

用于编写测试的类位于“unittest”模块中。如果您使用的是旧版的 Python(早于 Python 2.1),则可以 http://pyunit.sourceforge.net/ 下载该模块。但是,unittest 模块现在是标准 Python 发行版的一部分;因此它不需要单独安装。

UnitTest Framework - Framework

“unittest” 支持测试自动化、测试的设置和关闭代码共享、测试聚合到集合中以及将测试独立于报告框架。

unittest 模块提供了使为一组测试轻松支持这些质量的类。

为实现此目的,unittest 支持以下重要概念 −

  1. test fixture − 这表示执行一项或多项测试以及任何关联的清理操作所需的准备工作。例如,它可能涉及创建临时或代理数据库、目录或者启动服务器进程。

  2. test case − 这是测试的最小单元。它会检查针对特定的一组输入的特定响应。unittest 提供了一个基类 TestCase ,该类可以用于创建新的测试用例。

  3. test suite − 这是测试用例、测试套件或两者的集合。这用于聚合应该一起执行的测试。测试套件是由 TestSuite 类实现的。

  4. test runner − 这是一个对测试执行进行编排并且向用户提供结果的组件。运行程序可以使用图形界面、文本界面或返回一个特殊值以指示执行测试的结果。

Creating a Unit Test

编写一个简单单元测试涉及以下步骤 −

Step 1 − 在您的程序中导入 unittest 模块。

Step 2 − 定义要测试的函数。在以下示例中,add() 函数将受测试。

Step 3 − 通过继承 unittest.TestCase 创建一个测试用例。

Step 4 − 在类中将一个测试定义为方法。方法的名称必须以 “test” 开头。

Step 5 − 每个测试都会调用 TestCase 类的 assert 函数。有许多类型的断言。以下示例调用 assertEquals() 函数。

Step 6 − assertEquals() 函数将 add() 函数的结果与 arg2 参数进行比较,并且如果比较失败,则会引发 assertionError。

Step 7 − 最后,从 unittest 模块调用 main() 方法。

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 − 从命令行运行上述脚本。

C:\Python27>python SimpleTest.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK

Step 9 − 测试有以下三种可能的结果 −

Sr.No

Message & Description

1

OK 测试通过。在控制台上显示“A”。

2

FAIL 测试未通过,并引发 AssertionError 异常。在控制台上显示“F”。

3

ERROR 测试引发 AssertionError 以外的其他异常。在控制台上显示“E”。

这些结果分别在控制台上显示为“.”、“F”和“E”。

Command Line Interface

可以从命令行使用 unittest 模块运行单个或多个测试。

python -m unittest test1
python -m unittest test_module.TestClass
python -m unittest test_module.TestClass.test_method

unittest 支持以下命令行选项。要列出所有命令行选项,请使用以下命令

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 在第一个失败时停止

5

-c, --catch 捕获 Ctrl-C 并在控制台上显示结果

6

-b, --buffer 在测试运行期间缓冲 stdout 和 stderr

UnitTest Framework - API

本章讨论 unittest 模块中定义的类和方法。此模块中有五个主要类。

TestCase Class

此类的对象表示最小的可测试单元。它包含测试例程,并提供挂钩以便准备各个例程和清理之后的内容。

TestCase 类中定义了以下方法 −

Sr.No

Method & Description

1

setUp() 用于准备测试装置的方法。这会在调用测试方法前立即调用

2

tearDown() 在调用测试方法并记录结果后立即调用的方法。即使测试方法引发异常,也会调用此方法,

3

setUpClass() 个人类运行中的测试前调用的类方法。

4

tearDownClass() 个人类运行中的测试后调用的类方法。

5

run(result = None) 运行测试,将结果收集到作为结果传递的测试结果对象中。

6

skipTest(reason) 在测试方法或 setUp() 期间调用此方法将跳过当前测试。

7

debug() 不收集结果地运行测试。

8

shortDescription() 返回测试的一行描述。

Fixtures

一个 TestCase 类内可以编写很多测试。这些测试方法可能需要数据库连接、临时文件或其他资源初始化。这些称为固定装置。TestCase 包含一个特殊钩子来配置和清理你的测试所需的任何固定装置。要配置固定装置,请覆盖 setUp()。要清理,请覆盖 tearDown()。

在下面的示例中,在 TestCase 类内编写了两个测试。它们测试两个值相加和相减的结果。setup() 方法基于每个测试的 shortDescription() 初始化参数。teardown() 方法将在每个测试的末尾执行。

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()

从命令行运行以上代码。它给出以下输出 −

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 指令装饰。

下面的示例演示了这些类方法的使用 −

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 类提供。

创建和运行测试套件包括以下步骤。

Step 1 − 创建 TestSuite 类的实例。

suite = unittest.TestSuite()

Step 2 − 在套件内添加 TestCase 类中的测试。

suite.addTest(testcase class)

Step 3 − 你还可以使用 makeSuite() 方法从一个类添加测试

suite = unittest.makeSuite(test case class)

Step 4 − 个体测试也可以添加到套件中。

suite.addTest(testcaseclass(""testmethod")

Step 5 − 创建 TestTestRunner 类的对象。

runner = unittest.TextTestRunner()

Step 6 − 调用 run() 方法运行套件中的所有测试,将结果收集到测试结果对象中

runner.run (suite)

TestSuite 类中定义了以下方法 −

Sr.No

Method & Description

1

addTest() 在测试套件中添加一个测试方法。

2

addTests() 从多个 TestCase 类添加测试。

3

run() 运行与此套件关联的测试,将结果收集到测试结果对象中

4

debug() 不收集结果,运行与此套件关联的测试。

5

countTestCases() 返回此测试对象表示的测试数量

下面的示例说明了如何使用 TestSuite 类:

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() 方法的行和注释语句的注释来进行实验。

TestLoader Class

unittest 包具有 TestLoader 类,用于根据类和模块创建测试套件。默认情况下,在调用 unittest.main(0 方法时自动创建 unittest.defaultTestLoader 实例。但是,显式实例可以自定义某些属性。

在以下代码中,使用 TestLoader 对象将来自两个类的测试收集到一个列表中。

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 类中的方法列表:

S.No

Method & Description

1

loadTestsFromTestCase() 返回 TestCase 类中包含的所有测试用例的套件

2

loadTestsFromModule() 返回给定模块中包含的所有测试用例的套件。

3

loadTestsFromName() 根据字符串说明符返回所有测试用例的套件。

4

discover() 通过从指定的起始目录递归到子目录中来查找所有测试模块,并返回一个 TestSuite 对象

TestResult Class

此类用于编译有关已成功测试和已失败测试的信息。TestResult 对象存储一组测试的结果。TestResult 实例由 TestRunner.run() 方法返回。

TestResult 实例具有以下属性:

Sr.No

Attribute & Description

1

Errors 包含 TestCase 实例及其保存已格式化回溯的字符串的 2 元组的列表。每个元组代表引发意外异常的测试。

2

Failures 包含 TestCase 实例及其保存已格式化回溯的字符串的 2 元组的列表。每个元组代表使用 TestCase.assert*() 方法明确发出失败信号的测试。

3

Skipped 包含 TestCase 实例及其保存跳过测试的理由的字符串的 2 元组的列表。

4

wasSuccessful() 如果到目前为止运行的所有测试都已通过,则返回 True,否则返回 False。

5

stop() 可以调用此方法来发出应该中止正在运行的测试集的信号。

6

startTestRun() 在执行任何测试之前调用一次。

7

stopTestRun() 在所有测试执行完毕后调用一次。

8

testsRun 到目前为止运行的总测试数量。

9

Buffer 若设置为真,则 sys.stdoutsys.stderr 将分别在 startTest() 和 stopTest() 被调用之间进行缓冲。

以下代码执行了一个测试套件 −

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"

代码执行后显示以下输出 −

---- 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。然后,测试框架会将测试识别为失败。其他异常被视为错误。

在 unittest 模块中定义了以下三组断言函数:

  1. Basic Boolean Asserts

  2. Comparative Asserts

  3. Asserts for Collections

基本断言函数评估运算结果是 True 还是 False。所有 assert 方法都接受一个 msg 参数,如果指定,则该参数在失败时用作错误消息。

Sr.No

Method & Description

1

assertEqual(arg1, arg2, msg = None) 测试 arg1 和 arg2 相等。如果这两个值不相等,则测试将失败。

2

assertNotEqual(arg1, arg2, msg = None) 测试 arg1 和 arg2 不相等。如果这两个值相等,则测试将失败。

3

assertTrue(expr, msg = None) 测试 expr 为 true。如果为 false,则测试失败

4

assertFalse(expr, msg = None) 测试 expr 为 false。如果为 true,测试失败

5

assertIs(arg1, arg2, msg = None) 测试 arg1 和 arg2 评估为同一个对象。

6

assertIsNot(arg1, arg2, msg = None) 测试 arg1 和 arg2 未评估为同一个对象。

7

assertIsNone(expr, msg = None) 测试 expr 为 None。如果不为 None,测试失败

8

assertIsNotNone(expr, msg = None) 测试 expr 不为 None。如果为 None,测试失败

9

assertIn(arg1, arg2, msg = None) 测试 arg1 在 arg2 中。

10

assertNotIn(arg1, arg2, msg = None) 测试 arg1 不在 arg2 中。

11

assertIsInstance(obj, cls, msg = None) 测试 obj 是 cls 的实例

12

assertNotIsInstance(obj, cls, msg = None) 测试 obj 不是 cls 的实例

上面的部分断言函数在下面的代码中实现 −

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 将显示失败,其他将会成功运行。

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 −

  1. assertAlmostEqual (first, second, places = 7, msg = None, delta = None)通过计算差异测试 first 和 second 是否约等于(或不约等于),并四舍五入到指定的小数位数(默认为 7),

  2. assertNotAlmostEqual (first, second, places, msg, delta)通过计算差异测试 first 和 second 是否不约等于,并四舍五入到指定的小数位数(默认为 7),并与 0 比较。在上述两个函数中,如果提供了 delta 而不是 places,则 first 和 second 之间的差异必须小于或等于(或大于)delta。同时提供 delta 和 places 将引发 TypeError。

  3. assertGreater (first, second, msg = None)根据方法名称测试 first 是否大于 second。如果不是,则测试失败。

  4. assertGreaterEqual (first, second, msg = None)根据方法名称测试 first 是否大于或等于 second。如果不是,则测试失败

  5. assertLess (first, second, msg = None)根据方法名称测试 first 是否小于 second。如果不是,则测试失败

  6. assertLessEqual (first, second, msg = None)根据方法名称测试 first 是否小于或等于 second。如果不是,则测试失败。

  7. assertRegexpMatches (text, regexp, msg = None)测试正则表达式搜索是否匹配文本。在失败的情况下,错误消息将包括模式和文本。regexp 可能是一个正则表达式对象或一个包含适合 re.search() 使用的正则表达式的字符串。

  8. assertNotRegexpMatches (文本、regexp、msg = 无)验证一个 regexp 搜索不匹配文本。失败时显示包括模式和文本匹配部分的错误消息。regexp 可能是一个正则表达式对象,或者是一个包含正则表达式的字符串,适合 re.search() 使用。

断言函数在以下示例中实现 −

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。

=====================================================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。

Sr.No

Method & Description

1

assertListEqual (list1, list2, msg = None) 测试两个列表是否相等。如果不相等,则构造一条仅显示两者之间差异的错误消息。

2

assertTupleEqual (tuple1, tuple2, msg = None) 测试两个元组是否相等。如果不相等,则构造一条仅显示两者之间差异的错误消息。

3

assertSetEqual (set1, set2, msg = None) 测试两个集合是否相等。如果不相等,则构造一条列出集合之间差异的错误消息。

4

assertDictEqual (expected, actual, msg = None) 测试两个字典是否相等。如果不相等,则构造一条显示字典之间差异的错误消息。

以下示例实现了上述方法 −

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 对象中的差异。

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 测试框架将此函数用于简单的测试发现。为了保持兼容性,包含测试的模块和包必须可以从顶级目录导入。

以下适用于测试发现的基本命令行用法 −

Python –m unittest discover

解释器将尝试递归地加载所有包含测试的当前目录和内部目录中的模块。其他命令行选项 −

Sr.No

Options & Description

1

-v, --verbose Verbose output

2

-s, --start-directory directory 用于启动发现的目录(默认为 .)

3

-p, --pattern pattern 匹配测试文件(测试 * .py 默认)的模式

4

-t, --top-level-directory directory 项目的顶级目录(默认为启动目录)

例如,为了发现“tests”目录中名称以“assert”开头的模块中的测试,使用了以下命令行:

C:\python27>python –m unittest –v –s "c:\test" –p "assert*.py"

测试发现通过导入测试来加载测试。一旦测试发现从您指定的启动目录中找到了所有测试文件,它就会将路径转换为要导入的包名。

如果您将启动目录作为包名而不是路径提供给目录,则发现会假设从中导入的任何位置都是您想要的位置,因此您不会收到警告。

UnitTest Framework - Skip Test

自 Python 2.7 以来,已添加了跳过测试的支持。可以有条件地或无条件地跳过单个测试方法或 TestCase 类。该框架允许将某个测试标记为“预期故障”。此测试将“失败”,但不会被计为 TestResult 中的失败。

要无条件地跳过一个方法,可以使用以下 unittest.skip() 类方法:

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() 是一个类方法,因此它之前带有 @ 令牌。此方法接受一个参数:描述跳过原因的日志消息。

当执行以上脚本时,将在控制台上显示以下结果:

C:\Python27>python skiptest.py
s
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK (skipped = 1)

字符“s”表示已跳过某个测试。

跳过测试的替代语法是在测试函数内使用实例方法 skipTest()。

def testadd2(self):
   self.skipTest("another method for skipping")
   self.assertTrue(add(4 + 5) == 10)

以下装饰器实现测试跳过和预期失败:

Sr.No

Method & Description

1

unittest.skip(reason) 无条件跳过装饰后的测试。reason 应说明跳过测试的原因。

2

unittest.skipIf(condition, reason) 如果条件为真,则跳过装饰后的测试。

3

unittest.skipUnless(condition, reason) 只有在条件为真时才跳过装饰后的测试。

4

unittest.expectedFailure() 将测试标记为预期失败。如果测试运行时失败,则该测试不会被计为失败。

下面的示例演示了如何使用条件跳过和预期失败。

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() 已被标记为预期失败。

当运行上述脚本时,两个跳过的测试将显示“s”,而预期失败将显示为“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 测试框架提供以下断言方法来检查是否引发了异常。

assertRaises(exception, callable, *args, **kwds)

测试当调用函数时抛出异常(第一个参数),则只要有位置或关键字参数。当抛出预期的异常时测试通过,如果抛出其他异常则报错,如果没有抛出异常则测试失败。为了捕获一组异常,可将包含异常类的元组作为异常传递。

在下面的示例中,定义一个测试函数来检查是否抛出 ZeroDivisionError。

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() 函数的参数更改如下 −

self.assertRaises(ZeroDivisionError, div, 1,1)

当使用这些更改运行代码时,测试失败,因为不会出现 ZeroDivisionError。

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() 使用。

以下示例演示如何使用 assertRaisesRegexp() −

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” 可以在第二个参数字符串中找到。

================================================================
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
----------------------------------------------------------------------

但是,更改如下所示 −

self.assertRaisesRegexp(TypeError, "invalid", reg,123,"TutorialsPoint")

会抛出 TypeError 异常。因此,会显示以下结果 −

================================================================
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 的实现)有一个便捷的超时选项。如果测试花费的时间超过指定的时间,则该测试将标记为失败。

Python 的测试框架不包含任何超时支持。但是,一个名为 timeout-decorator 的第三方模块可以完成该作业。

从以下位置下载并安装该模块 −

  1. 在代码中导入 timeout_decorator

  2. 在测试之前使用超时修饰器

  3. @timeout_decorator.timeout(10)

如果这个时间段中的测试方法超过这里提到的超时(10 分钟),将会引发 TimeOutError。例如 −

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,而不是 unittest,请用 import unittest2 替换 import unittest。

unittest2 中的类派生自 unittest 中相应的类,因此可以直接使用 unittest2 测试运行基础设施,无需立即将所有测试切换为使用 unittest2。如果你打算实现新功能,请将你的测试用例从 unittest2.TestCase 子类化,而不是 unittest.TestCase

以下是 unittest2 的新功能 −

  1. addCleanups 用于更好的资源管理

  2. 包含许多新的断言方法

  3. assertRaises 作为上下文管理器,可随后访问异常

  4. 具有 setUpModuletearDownModule 等模块级装配夹具

  5. 包括 load_tests 协议,用于从模块或软件包中加载测试

  6. 测试结果中的 startTestRunstopTestRun 方法

在 Python 2.7 中,你可以使用 python -m unittest <args> 调用 unittest 命令行功能(包括测试发现)。

而 unittest2 附带了一个脚本 unit2。

unit2 discover
unit2 -v test_module

UnitTest Framework - Signal Handling

通过使用 -c/--catch 命令行选项与 catchbreak 参数,在测试运行期间可以提高对 control-C 的处理效率。启用 catch break 行为后,control-C 将允许当前正在运行的测试完成,然后测试运行将结束并报告迄今为止的所有结果。第二次 control-c 将按通常方式引发 KeyboardInterrupt。

如果调用了 unittest 处理程序但未安装 signal.SIGINT 处理程序,则它将调用默认处理程序。这通常是替换已安装处理程序并委派给它的代码的预期行为。对于需要禁用 unittest control-c 处理的各个测试,可以使用 removeHandler() 装饰器。

以下实用函数在测试框架中启用 control-c 处理功能:

unittest.installHandler()

安装 control-c 处理程序。当接收到 signal.SIGINT 时,会调用所有已注册结果的 TestResult.stop()。

unittest.registerResult(result)

注册 TestResult 对象以进行 control-c 处理。注册结果将存储对其的弱引用,因此不会阻止垃圾收集该结果。

unittest.removeResult(result)

移除已注册的结果。移除结果后,control-c 将不再响应该结果对象调用 TestResult.stop()。

unittest.removeHandler(function = None)

在未带参数调用时,如果已安装此函数,此函数将移除 control-c 处理程序。此函数还可以用作测试装饰器,以便在执行测试时临时移除处理程序。

GUI Test Runner

安装 unittest 模块以交互方式发现并运行测试。此实用程序,Python 脚本“inittestgui.py”,使用了 Tkinter 模块,它是 TK 图形工具包的 Python 移植。它提供了一个易于使用的 GUI,用于发现和运行测试。

Python unittestgui.py
running tests

单击“发现测试”按钮。将出现一个小的对话框,您可以在其中选择要运行测试的目录和模块。

discover tests

最后,点击“开始”按钮。将会从选定的路径和模块名称发现测试,结果窗格将显示结果。

result pane

为了看到单个测试的详细信息,选择并单击结果框中的测试即可 −

individual test details

如果在 Python 安装中找不到此实用程序,可以从项目页面 http://pyunit.sourceforge.net/ 获取。

类似基于 wxpython 工具包的实用程序也可在该处获得。

UnitTest Framework - Doctest

Python 的标准发行版包含“Doctest”模块。此模块的功能使其能够搜索看起来像交互式 Python 会话的文本片断,并执行这些会话以查看它们是否完全按照显示的那样工作。

Doctest 在以下场景中非常有用 −

  1. 通过验证所有交互式示例仍然按文档所述工作,来检查模块的文档字符串是否是最新的。

  2. 通过验证来自测试文件或测试对象的交互式示例按预期工作,来执行回归测试。

  3. 为软件包编写教程文档,大量展示输入输出示例

在 Python 中,‘文档字符串’是一个字符串文字,它作为类,函数或模块中的第一个表达式出现。当执行该套件时,它会被忽略,但会被编译器识别并放入封闭类,函数或模块的 doc 属性中。由于它是通过自省获得的,因此它是文档对象规则的地方。

在 Python 代码不同部分使用示例用法的通常做法是将其放入文档字符串中。doctest 模块允许验证这些文档字符串在代码之间断断续续的修订中是处于最新状态。

在下面的代码中,一个阶乘函数与示例用法穿插定义。为了验证示例用法是否正确,请在 doctest 模块中调用 testmod() 函数。

"""
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,尝试从命令行执行此脚本。

Python FactDocTest.py

除非示例失败,否则不会显示任何输出。现在,将命令行更改为以下内容:

Python FactDocTest.py –v

控制台现在会显示以下输出:

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。结果如下:

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() 函数完成。

以下文本存储在名为 "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() 函数。

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")
  1. 与 testmod() 一样,testfile() 只会在示例失败时显示内容。如果某个示例确实失败,则失败示例及其失败原因将按照与 testmod() 相同的格式打印到控制台。

  2. 在大多数情况下,交互式控制台会话的复制和粘贴都能正常工作,但 doctest 并不是试图对任何特定的 Python shell 进行精确仿真。

  3. 任何预期的输出都必须紧跟包含代码的最终 ">>> " 或 "…​ " 行,并且预期输出(如果有)会延伸到下一个 ">>> " 或全空白行。

  4. 预期输出不能包含全空白行,因为这样的行被认为是预期输出的结束信号。如果预期输出确实包含一个空行,请在您的 doctest 示例中将 <BLANKLINE> 放在预期空行出现的每个位置。

UnitTest Framework - Doctest API

doctest API 围绕下列两个容器类进行,用于从 docstring 中存储交互示例 −

  1. Example − 一个单独的 Python 语句,与预期的输出配对。

  2. DocTest − 一个示例集合,通常从单个 docstring 或文本文件中提取。

定义了以下附加处理类,用于查找、解析并运行以及检查 doctest 示例 −

  1. DocTestFinder − 查找给定模块中的所有 docstring,并使用 DocTestParser 从包含交互示例的每个 docstring 创建一个 DocTest。

  2. DocTestParser − 从字符串(如某个对象的 docstring)创建 doctest 对象。

  3. DocTestRunner − 执行 doctest 中的示例,并使用 OutputChecker 验证其输出。

  4. OutputChecker − 将 doctest 示例的实际输出与预期输出进行比较,并确定它们是否匹配。

DocTestFinder Class

它是一个处理类,用于从 docstring 及其包含对象 docstring 中提取与给定对象相关的 doctest。当前可以从以下对象类型中提取 doctest — 模块、函数、类、方法、静态方法、类方法和属性。

此类定义 find() 方法。它返回由对象的 docstring,或其包含对象的任何 docstring 定义的 DocTest 列表。

DocTestParser Class

这是一个处理类,用于从字符串中提取交互示例,并使用它们来创建 DocTest 对象。该类定义了以下方法 -

  1. get_doctest() - 从给定字符串中提取所有 doctest 示例,并将它们收集到 DocTest 对象中。

  2. get_examples(string[, name]) - 从给定字符串中提取所有 doctest 示例,并将它们作为 Example 对象列表返回。行号采用 0 为基准。可选参数名称是一个标识此字符串的名称,并且仅用于错误消息。

  3. parse(string[, name]) - 将给定字符串划分为示例和干预文本,并将它们作为 Examples 和字符串的交替列表返回。 Examples 的行号采用 0 为基准。可选参数名称是一个标识此字符串的名称,并且仅用于错误消息。

DocTestRunner Class

这是一个处理类,用于执行和验证 DocTest 中的交互示例。其中定义了以下方法 -

report_start()

报告测试运行器即将处理给定示例。此方法是为了允许 DocTestRunner 的子类自定义其输出;不应该直接调用它

report_success()

报告给定的示例运行成功。此方法是为了允许 DocTestRunner 的子类自定义其输出;不应该直接调用它

report_failure()

报告给定的示例失败。此方法是为了允许 DocTestRunner 的子类自定义其输出;不应该直接调用它

report_unexpected_exception()

报告给定的示例引发了一个意外的异常。此方法用于允许 DocTestRunner 的子类自定义其输出;不应直接调用它。

run(test)

运行测试中的示例(一个 DocTest 对象),并使用 writer 函数 out 显示结果

summarize([verbose])

打印此 DocTestRunner 运行的所有测试用例的摘要,并返回一个命名的元组 TestResults(failed, attempted)。可选的 verbose 参数控制摘要的详细程度。如果未指定详细程度,则使用 DocTestRunner 的详细程度。

OutputChecker Class

此类用于检查 doctest 示例的实际输出是否与预期输出匹配。

此类中定义了以下方法 -

check_output()

如果示例(got)的实际输出与预期输出(want)匹配,则返回 True 。如果这些字符串相同,则它们始终被认为匹配;但是,根据测试运行器使用的选项标记是什么,也可能使用多种非精确匹配类型。有关选项标记的详细信息,请参阅选项标记和指令部分。

output_difference()

返回一个字符串,描述给定示例(example)的预期输出与实际输出(got)之间的差异。

DocTest Integration with Unittest

doctest 模块提供了两个函数,可用于从包含 doctest 的模块和文本文件中创建 unittest 测试套件。若要与 unittest 测试发现集成,请在您的测试模块中包含一个 load_tests() 函数 -

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() 方法来执行它。

以下是用于从文本文件和包含 doctest 的模块创建 unittest.TestSuite 实例的两个主要函数 -

doctest.DocFileSuite()

它用于将一个或多个文本文件中的 doctest 测试转换为 unittest.TestSuite 。返回的 unittest.TestSuite 由 unittest 框架运行,并在每个文件中运行交互式示例。如果文件中任何示例失败,则合成的单元测试也将失败,并且会引发一个 failureException 异常,该异常显示包含测试的文件的名称和(有时是近似值)行号。

doctest.DocTestSuite()

它用于将模块的 doctest 测试转换为 unittest.TestSuite

返回的 unittest.TestSuite 由 unittest 框架运行,并且运行模块中的每个 doctest。如果任何 doctest 失败,则合成的单元测试失败,并且引发 failureException 异常,显示包含测试的文件名称和(有时近似)行号

DocTestSuite() 在底层创建一个 unittest.TestSuite ,该对象由 doctest.DocTestCase 实例和 DocTestCase(unittest.TestCase 的子类)组成。

类似地,DocFileSuite() 使用 doctest.DocFileCase 实例创建一个 unittest.TestSuite,而 DocFileCase 是 DocTestCase 的子类。

因此,两种创建 unittest.TestSuite 的方式都运行 DocTestCase 的实例。通过将选项标志传递给 doctest 函数,当您自己运行 doctest 函数时,您可以直接控制所使用的 doctest 选项。

但是,如果您正在编写一个 unittest 框架,unittest 最终会控制何时以及如何运行测试。框架作者通常想要控制 doctest 报告选项(例如,由命令行选项指定),但无法通过 unittest 将选项传递给 doctest 测试运行程序。

UnitTest Framework - Py.test Module

2004 年,Holger Krekel 将其 std 包重命名为“py”(仅略微没那么容易混淆),因为该名称经常与随 Python 一起提供的标准库混淆。尽管此包包含多个子包,但直到现在,它几乎完全以其 py.test 框架而闻名。

py.test 框架为 Python 测试制定了新的标准,如今已受到许多开发者的欢迎。它为编写测试而引入的优雅且具有 Pythonic 特性的习语使得测试套件可以使用更为简洁的风格编写。

py.test 是 Python 标准 unittest 模块的无样板代码替代方案。它作为功能完备且可扩展的测试工具,其语法简洁。创建测试套件就像编写具有几个函数的模块一样简单。

py.test 可以在所有 POSIX 操作系统和 Windows (XP/7/8) 上运行,并支持 Python 2.6 及以上版本。

Installation

使用以下代码即可在当前 Python 分发中加载 pytest 模块以及 py.test.exe 实用程序。可以使用这两种方式运行测试。

pip install pytest

Usage

你可以使用 assert 语句为测试期望提供断言。pytest 的断言内省将智能地报告 assert 表达式的中间值,从而使你无需学习 JUnit legacy methods 的许多名称。

# content of test_sample.py
def func(x):
   return x + 1

def test_answer():
   assert func(3) == 5

使用以下命令行运行上述测试。运行测试后,将在控制台上显示以下结果

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 模块从命令行运行测试。

python -m pytest test_sample.py

Grouping Multiple Tests in a Class

一旦开始拥有多个测试,通常可按逻辑将这些测试分组为类和模块。让我们编写一个包含两个测试的类

class TestClass:
   def test_one(self):
      x = "this"
      assert 'h' in x
   def test_two(self):
      x = "hello"
      assert hasattr(x, 'check')

将显示以下测试结果 −

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 早已开创的测试惯用语,但其是一个更容易安装和维护的包。

nose 模块借助 pip 实用程序可以进行安装。

pip install nose

这将在当前 Python 发行版和 nosetest.exe 中安装 nose 模块,这意味着可以使用该实用程序和 –m 开关来运行该测试。

C:\python>nosetests –v test_sample.py
Or
C:\python>python –m nose test_sample.py

nose 当然会从 unittest.TestCase 子类中收集测试。而且可以编写简单的测试函数,以及不是 unittest.TestCase 子类的测试类。nose 还提供了一些有用的函数,以编写时序测试、针对异常的测试以及其他常见用例。

nose 会自动收集测试。无需手动将测试用例收集至测试套件。运行测试具有响应性,因为 nose 会在加载第一个测试模块后立即开始运行测试。

与 unittest 模块一样, nose 也支持包、模块、类和测试用例级别的 fixture,因此可以尽少地进行开销较大的初始化。

Basic Usage

我们来看一下与之前所用脚本相类似的 nosetest.py −

# content of nosetest.py
def func(x):
   return x + 1

def test_answer():
   assert func(3) == 5

要运行上述测试,请使用以下命令行语法 −

C:\python>nosetests –v nosetest.py

控制台上的输出如下 −

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 进行集成。

\nosetests --with-doctest -v nosetest.py

你可以在测试脚本中使用 nose

import nose
nose.main()

如果你不想让测试脚本在成功时退出为 0,在失败时退出为 1(如 unittest.main),请改用 nose.run()−

import nose
result = nose.run()

如果测试运行成功,则结果为 true;如果失败或引发未捕获的异常,则结果为 false。

nose 在包、模块、类和测试级别支持 fixture(setUp 和 tearDown 方法)。与 py.test 或 unittest fixture 一样,setUp 会始终在任何测试运行之前运行(对于测试包和模块,则在测试集合之前运行);tearDown 会在 setUp 顺利完成后运行,而与测试运行状态无关。

Nose Testing - Tools

nose.tools 模块提供了一些您可能发现有用的测试辅助工具,包括用于限制测试执行时间和测试异常的装饰器,以及 unittets.TestCase 中的所有同类 assertX 方法。

  1. nose.tools.ok_(expr, msg = None) − assert 的速记。

  2. nose.tools.eq_(a, b, msg = None) − “assert a == b, “%r != %r” % (a, b)”的速记

  3. nose.tools.make_decorator(func) − 封装测试装饰器,以便正确复制所装饰函数的元数据信息,包括 nose 的附加信息(即,setup 和 teardown)。

  4. nose.tools.raises(*exceptions) − 测试必须引发某个预期异常才能通过。

  5. nose.tools.timed(limit) − 测试必须在指定的时间限制内完成才能通过

  6. nose.tools.istest(func) − 将函数或方法标记为测试的装饰器

  7. nose.tools.nottest(func) − 将函数或方法标记为非测试的装饰器

Parameterized Testing

Python 的测试框架 unittest 没有一个简单的方法来运行参数化测试用例。换句话说,您无法轻松从外部向 unittest.TestCase 传递参数。

然而,pytest 模块通过几种完全集成的途径实现测试参数传递 −

  1. pytest.fixture() 允许您在 fixture 函数级别定义参数传递。

  2. @pytest.mark.parametrize 允许在函数或类级别定义参数传递。它为特定的测试函数或类提供多个参数/fixture。

  3. pytest_generate_tests 使您能够实现自己的自定义动态参数传递方案或扩展。

第三方模块“nose-parameterized”允许对任何 Python 测试框架进行参数化测试。可以从以下链接下载它 − https://github.com/wolever/nose-parameterized