单元测试框架 - 快速指南



单元测试框架 - 概述

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

单元测试可以通过以下两种方式进行:

手动测试 自动化测试

在没有任何工具支持的情况下手动执行测试用例称为手动测试。

  • 由于测试用例是由人力资源执行的,因此非常耗时且繁琐

  • 由于需要手动执行测试用例,因此手动测试需要更多测试人员。

  • 它不太可靠,因为由于人为错误,每次测试的精确度可能不一致。

  • 无法编写获取隐藏信息的复杂测试程序。

利用工具支持并使用自动化工具执行测试用例称为自动化测试。

  • 快速的自动化比人力资源快得多地运行测试用例。

  • 对人力资源的投资较少,因为测试用例是使用自动化工具执行的。

  • 自动化测试每次运行时执行完全相同的操作,并且更可靠

  • 测试人员可以编写复杂的测试程序来获取隐藏信息。

JUnit 是 Java 编程语言的单元测试框架。JUnit 在测试驱动开发的发展中非常重要,并且是 xUnit 系列单元测试框架中的一员,该系列源自 JUnit。您可以在此处找到JUnit 教程

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

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

本教程是为初学者准备的,以帮助他们了解 Python 测试框架的基本功能。完成本教程后,您将掌握使用 Python 测试框架的中等水平的专业知识,您可以从这里提升到更高的水平。

您应该具备使用 Python 语言进行软件开发的合理专业知识。我们的Python 教程是学习 Python 的一个好地方。了解软件测试的基础知识也是可取的。

环境设置

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

单元测试框架 - 框架

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

unittest 模块提供了一些类,这些类使得轻松支持这些测试集合的特性变得容易。

为了实现这一点,unittest 支持以下重要概念:

  • 测试夹具 - 这表示执行一个或多个测试所需的准备工作以及任何相关的清理操作。例如,这可能包括创建临时或代理数据库、目录或启动服务器进程。

  • 测试用例 - 这是测试的最小单元。这检查特定输入集的特定响应。unittest 提供了一个基类TestCase,可用于创建新的测试用例。

  • 测试套件 - 这是一个测试用例、测试套件或两者的集合。它用于聚合应一起执行的测试。测试套件由 TestSuite 类实现。

  • 测试运行器 - 这是一个协调测试执行并将结果提供给用户的组件。运行器可以使用图形界面、文本界面或返回特殊值来指示测试执行的结果。

创建单元测试

编写简单的单元测试涉及以下步骤:

步骤 1 - 在您的程序中导入 unittest 模块。

步骤 2 - 定义要测试的函数。在下面的示例中,add() 函数将进行测试。

步骤 3 - 通过子类化 unittest.TestCase 创建测试用例。

步骤 4 - 将测试定义为类中的方法。方法名称必须以 'test' 开头。

步骤 5 - 每个测试都调用 TestCase 类的 assert 函数。有许多类型的断言。下面的示例调用 assertEquals() 函数。

步骤 6 - assertEquals() 函数将 add() 函数的结果与 arg2 参数进行比较,如果比较失败则抛出 AssertionError 异常。

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

步骤 8 - 从命令行运行上述脚本。

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

步骤 9 - 测试可能产生的三种结果:

序号 消息和描述
1

OK

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

2

FAIL

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

3

ERROR

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

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

命令行界面

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

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

unittest 支持以下命令行选项。有关所有命令行选项的列表,请使用以下命令:

Python –m unittest -h

序号 选项和描述
1

-h, --help

显示此消息

2

v, --verbose

详细输出

3

-q, --quiet

最小输出

4

-f, --failfast

在第一次失败时停止

5

-c, --catch

捕获 Control-C 并显示结果

6

-b, --buffer

在测试运行期间缓冲标准输出和标准错误

单元测试框架 - API

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

TestCase 类

此类的对象表示最小的可测试单元。它包含测试例程,并提供用于准备每个例程和随后清理的挂钩。

TestCase 类中定义了以下方法:

序号 方法和描述
1

setUp()

调用此方法以准备测试夹具。这在调用测试方法之前立即调用

2

tearDown()

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

3

setUpClass()

在运行单个类中的测试之前调用的类方法。

4

tearDownClass()

在运行单个类中的所有测试后调用的类方法。

5

run(result = None)

运行测试,并将结果收集到作为result传递的测试结果对象中。

6

skipTest(reason)

在测试方法或 setUp() 中调用此方法将跳过当前测试。

7

debug()

运行测试但不收集结果。

8

shortDescription()

返回测试的一行描述。

夹具

可以在 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)

类夹具

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 类

Python 的测试框架提供了一种有用的机制,可以通过它根据测试用例实例测试的功能将它们组合在一起。unittest 模块中的 TestSuite 类提供了这种机制。

创建和运行测试套件涉及以下步骤:

步骤 1 - 创建 TestSuite 类的实例。

suite = unittest.TestSuite()

步骤 2 - 将 TestCase 类中的测试添加到套件中。

suite.addTest(testcase class)

步骤 3 - 您还可以使用 makeSuite() 方法从类中添加测试

suite = unittest.makeSuite(test case class)

步骤 4 - 单个测试也可以添加到套件中。

suite.addTest(testcaseclass(""testmethod")

步骤 5 - 创建 TestTestRunner 类的对象。

runner = unittest.TextTestRunner()

步骤 6 - 调用 run() 方法以运行套件中的所有测试

runner.run (suite)

TestSuite 类中定义了以下方法:

序号 方法和描述
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)

您可以通过取消注释包含 makeSuite() 方法的行和注释语句来试验 addTest() 方法。

TestLoader 类

unittest 包包含 TestLoader 类,用于从类和模块创建测试套件。默认情况下,当调用 unittest.main() 方法时,会自动创建 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 类中方法的列表:

序号 方法和描述
1

loadTestsFromTestCase()

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

2

loadTestsFromModule()

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

3

loadTestsFromName()

返回给定字符串指定符的所有测试用例的套件。

4

discover()

从指定的起始目录递归进入子目录,查找所有测试模块,并返回一个 TestSuite 对象。

TestResult 类

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

TestResult 实例具有以下属性:

序号 属性与描述
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

如果设置为 true,则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

单元测试框架 - 断言

Python 测试框架使用 Python 的内置 assert() 函数来测试特定条件。如果断言失败,则会引发 AssertionError。然后,测试框架会将测试识别为失败。其他异常被视为错误。

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

  • 基本布尔断言
  • 比较断言
  • 集合断言

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

序号 方法和描述
1

assertEqual(arg1, arg2, msg = None)

测试arg1arg2 是否相等。如果值不相等,则测试将失败。

2

assertNotEqual(arg1, arg2, msg = None)

测试arg1arg2 是否不相等。如果值相等,则测试将失败。

3

assertTrue(expr, msg = None)

测试expr 是否为真。如果为假,则测试失败。

4

assertFalse(expr, msg = None)

测试expr 是否为假。如果为真,则测试失败。

5

assertIs(arg1, arg2, msg = None)

测试arg1arg2 是否评估为相同的对象。

6

assertIsNot(arg1, arg2, msg = None)

测试arg1arg2 是否不评估为相同的对象。

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)

第二组断言函数是比较断言:

  • assertAlmostEqual (first, second, places = 7, msg = None, delta = None)

    通过计算差值、四舍五入到给定的十进制位数places(默认为 7)并与零进行比较来测试firstsecond 是否近似(或不近似)相等。

  • assertNotAlmostEqual (first, second, places, msg, delta)

    通过计算差值、四舍五入到给定的十进制位数(默认为 7)并与零进行比较来测试 first 和 second 是否不近似相等。

    在上述两个函数中,如果提供 delta 而不是 places,则 first 和 second 之间的差值必须小于或等于(或大于)delta。

    同时提供 delta 和 places 将引发 TypeError。

  • assertGreater (first, second, msg = None)

    根据方法名称测试first 是否大于second。如果不是,则测试将失败。

  • assertGreaterEqual (first, second, msg = None)

    根据方法名称测试first 是否大于或等于second。如果不是,则测试将失败。

  • assertLess (first, second, msg = None)

    根据方法名称测试first 是否小于second。如果不是,则测试将失败。

  • assertLessEqual (first, second, msg = None)

    根据方法名称测试first 是否小于或等于second。如果不是,则测试将失败。

  • assertRegexpMatches (text, regexp, msg = None)

    测试正则表达式搜索是否与文本匹配。如果失败,错误消息将包含模式和文本。regexp 可以是正则表达式对象,也可以是包含适合re.search() 使用的正则表达式的字符串。

  • assertNotRegexpMatches (text, regexp, msg = None)

    验证regexp 搜索是否不匹配text。如果失败,则会显示一条错误消息,其中包含模式和与text 匹配的部分。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 的除法结果在 7 位小数内不等于 3.14。类似地,由于第二个参数与第一个参数中的文本匹配,因此 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)

集合断言

这组断言函数旨在与 Python 中的集合数据类型一起使用,例如列表、元组、字典和集合。

序号 方法和描述
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。错误消息显示列表和字典对象中的差异。

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)

单元测试框架 - 测试发现

TestLoader 类具有 discover() 函数。Python 测试框架使用此函数进行简单的测试发现。为了兼容,包含测试的模块和包必须可以从顶级目录导入。

以下是测试发现的基本命令行用法:

Python –m unittest discover

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

序号 选项与描述
1

-v, --verbose

详细输出

2

-s, --start-directory

directory 开始发现的目录(默认为 .)

3

-p, --pattern

pattern 匹配测试文件的模式(默认为 test*.py)

4

-t, --top-level-directory

directory 项目的顶级目录(默认为起始目录)

例如,为了发现名称以 'assert' 开头的模块中的测试,这些模块位于 'tests' 目录中,可以使用以下命令行:

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

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

如果您提供起始目录作为包名称而不是目录路径,则 discover 假定它从中导入的任何位置都是您想要的, 因此您不会收到警告。

单元测试框架 - 跳过测试

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

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

序号 方法和描述
1

unittest.skip(reason)

无条件跳过被装饰的测试。reason 应描述跳过测试的原因。

2

unittest.skipIf(condition, reason)

如果 condition 为真,则跳过被装饰的测试。

3

unittest.skipUnless(condition, reason)

除非 condition 为真,否则跳过被装饰的测试。

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)

单元测试框架 - 异常测试

Python 测试框架提供以下断言方法来检查是否引发了异常。

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

测试当用任何位置或关键字参数调用函数时是否引发异常(第一个参数)。如果引发预期异常,则测试通过;如果引发其他异常,则为错误;如果没有引发异常,则测试失败。要捕获任何一组异常,可以将包含异常类的元组作为 exception 传递。

在下面的示例中,定义了一个测试函数来检查是否引发了 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"
----------------------------------------------------------------------

单元测试框架 - 时间测试

JUnit(Java 单元测试框架,Pyunit 是 JUnit 的实现)有一个方便的超时选项。如果测试花费的时间超过指定时间,它将被标记为失败。

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

从以下地址下载并安装模块:

https://pypi.python.org/packages/source/t/timeout-decorator/timeout-decorator-0.3.2.tar.gz

  • 在代码中导入 timeout_decorator
  • 在测试之前放置 timeout 装饰器
  • @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()

单元测试框架 - Unittest2

unittest2 是对 Python 2.7 及更高版本中添加到 Python 测试框架的其他功能的反向移植。经过测试可在 Python 2.6、2.7 和 3.* 上运行。最新版本可以从 https://pypi.python.org/pypi/unittest2 下载

要使用 unittest2 代替 unittest,只需将 import unittest 替换为 import unittest2。

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

以下是 unittest2 的新功能:

  • addCleanups 用于更好的资源管理

  • 包含许多新的 assert 方法

  • assertRaises 作为上下文管理器,之后可以访问异常

  • 具有模块级 fixture,例如 setUpModuletearDownModule

  • 包括用于从模块或包加载测试的 load_tests 协议

  • TestResult 上的 startTestRunstopTestRun 方法

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

相反,unittest2 带有一个名为 unit2 的脚本。

unit2 discover
unit2 -v test_module

单元测试框架 - 信号处理

-c/--catch 命令行选项以及 catchbreak 参数为 unittest 提供了对测试运行期间 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)

注册用于 Control-C 处理的 TestResult 对象。注册结果会存储对它的弱引用,因此它不会阻止结果被垃圾回收。

unittest.removeResult(result)

删除已注册的结果。删除结果后,响应 Control-C 将不再在该结果对象上调用 TestResult.stop()。

unittest.removeHandler(function = None)

在不带参数调用时,此函数将删除已安装的 Control-C 处理程序。此函数也可以用作测试装饰器,以在测试执行期间暂时删除处理程序。

GUI 测试运行器

unittest 模块安装用于交互式地发现和运行测试。此实用程序(一个名为 'inittestgui.py' 的 Python 脚本)使用 Tkinter 模块,这是一个 Python 端口,用于 TK 图形工具包。它提供了一个易于使用的 GUI 用于发现和运行测试。

Python unittestgui.py
Running Test

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

Discover Test

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

Result Pane

要查看单个测试的详细信息,请选择结果框中的测试并单击它:

Individual Test Details

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

类似地,基于 wxpython 工具包的实用程序也在那里可用。

单元测试框架 - Doctest

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

Doctest 在以下情况下非常有用:

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

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

  • 为包编写教程文档,并大量使用输入输出示例进行说明

在 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 = 2 更改为 f = 1 并再次运行 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:检查文本文件中的示例

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")
  • 与 testmod() 一样,除非示例失败,否则 testfile() 不会显示任何内容。如果示例确实失败,则使用与 testmod() 相同的格式将失败的示例和失败的原因打印到控制台。

  • 在大多数情况下,交互式控制台会话的复制和粘贴效果很好,但 doctest 并不是试图对任何特定的 Python shell 进行精确模拟。

  • 任何预期的输出都必须紧跟包含代码的最后一个“>>>”或“... ”行之后,预期输出(如果有)延伸到下一个“>>>”或全空格行。

  • 预期输出不能包含全空格行,因为这样的行被认为表示预期输出的结束。如果预期输出确实包含空行,请在您的 doctest 示例中每个预期空行的位置放置

单元测试框架 - Doctest API

doctest API 围绕以下两个用于存储来自文档字符串的交互式示例的容器类展开:

  • Example - 单个 Python 语句,与其预期输出配对。

  • DocTest - 示例的集合,通常从单个文档字符串或文本文件提取。

定义以下附加处理类以查找、解析和运行以及检查 doctest 示例:

  • DocTestFinder - 在给定模块中查找所有文档字符串,并使用 DocTestParser 从包含交互式示例的每个文档字符串创建 DocTest。

  • DocTestParser - 从字符串(例如对象的文档字符串)创建 doctest 对象。

  • DocTestRunner - 执行 doctest 中的示例,并使用 OutputChecker 验证其输出。

  • OutputChecker − 比较 doctest 示例的实际输出与预期输出,并判断是否匹配。

DocTestFinder 类

这是一个处理类,用于从给定对象的文档字符串及其包含对象的文档字符串中提取与该对象相关的 doctest。目前可以从以下对象类型中提取 Doctests——模块、函数、类、方法、静态方法、类方法和属性。

此类定义了 find() 方法。它返回由对象的文档字符串或其任何包含对象的文档字符串定义的 DocTest 列表。

DocTestParser 类

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

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

  • get_examples(string[, name]) − 从给定字符串中提取所有 doctest 示例,并将它们作为Example 对象列表返回。行号从 0 开始。可选参数 name 用于标识此字符串,仅用于错误消息。

  • parse(string[, name]) − 将给定字符串划分为示例和中间文本,并将它们作为Examples 和字符串的交替列表返回。Examples 的行号从 0 开始。可选参数 name 用于标识此字符串,仅用于错误消息。

DocTestRunner 类

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

report_start()

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

report_success()

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

report_failure()

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

report_unexpected_exception()

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

run(test)

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

summarize([verbose])

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

OutputChecker 类

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

在此类中定义了以下方法:

check_output()

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

output_difference()

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

DocTest 与 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() 使用 doctest.DocTestCase 实例创建一个unittest.TestSuite,而 DocTestCase 是 unittest.TestCase 的子类。

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

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

但是,如果您正在编写 unittest 框架,unittest 最终会控制测试何时以及如何运行。框架作者通常希望控制 doctest 报告选项(例如,由命令行选项指定),但是没有办法通过 unittest 将选项传递给 doctest 测试运行器。

单元测试框架 - Py.test 模块

2004 年,Holger Krekel 将他的std 包(其名称经常与 Python 附带的标准库混淆)重命名为(只有略微不那么令人困惑的)名称“py”。尽管该包包含多个子包,但现在几乎完全以其 py.test 框架而闻名。

py.test 框架为 Python 测试设定了新的标准,并已成为当今许多开发人员非常流行的工具。它为测试编写引入的优雅且符合 Python 风格的习惯用法使得能够以更简洁的风格编写测试套件。

py.test 是 Python 标准 unittest 模块的一种无需样板代码的替代方案。尽管它是一个功能齐全且可扩展的测试工具,但它拥有简单的语法。创建测试套件就像编写一个包含几个函数的模块一样简单。

py.test 运行在所有 POSIX 操作系统和 WINDOWS(XP/7/8)上,Python 版本为 2.6 及更高版本。

安装

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

pip install pytest

用法

您可以简单地使用 assert 语句来断言测试预期。pytest 的 assert 自省功能将智能地报告 assert 表达式的中间值,使您无需学习JUnit 传统方法的许多名称。

# 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

在类中分组多个测试

当您开始拥有多个测试时,通常将测试逻辑地分组到类和模块中是有意义的。让我们编写一个包含两个测试的类:

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 测试 - 框架

nose 项目于 2005 年发布,也就是py.test 获得现代外观的次年。它由 Jason Pellerin 编写,用于支持 py.test 开创的相同测试习惯用法,但在一个更易于安装和维护的包中。

可以使用 pip 实用程序安装nose 模块。

pip install nose

这将在当前 Python 发行版中安装 nose 模块以及 nosetest.exe,这意味着可以使用此实用程序以及 –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 支持包、模块、类和测试用例级别的夹具,因此可以尽可能少地进行昂贵的初始化。

基本用法

让我们考虑与前面使用的脚本类似的 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)

可以通过在上述命令行中使用with-doctest 选项将nose 与 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 支持包、模块、类和测试级别的夹具(安装和拆卸方法)。与 py.test 或 unittest 夹具一样,安装总是在任何测试(或测试包和模块的测试集合)之前运行;如果安装成功完成,则无论测试运行的状态如何,拆卸都会运行。

Nose 测试 - 工具

nose.tools 模块提供许多您可能会觉得有用的测试辅助工具,包括用于限制测试执行时间和测试异常的装饰器,以及在 unittest.TestCase 中找到的所有相同的 assertX 方法。

  • nose.tools.ok_(expr, msg = None) − assert 的简写。

  • nose.tools.eq_(a, b, msg = None) − “assert a == b, “%r != %r” % (a, b)” 的简写。

  • nose.tools.make_decorator(func) − 包装测试装饰器,以便正确复制已装饰函数的元数据,包括 nose 的附加内容(即,安装和拆卸)。

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

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

  • nose.tools.istest(func) − 装饰器,用于将函数或方法标记为测试。

  • nose.tools.nottest(func) − 装饰器,用于将函数或方法标记为非测试。

参数化测试

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

但是,pytest 模块通过几种良好集成的方案移植了测试参数化:

  • pytest.fixture() 允许您在夹具函数级别定义参数化。

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

  • pytest_generate_tests 能够实现您自己的自定义动态参数化方案或扩展。

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

广告