1 概述

单元测试框架是一种软件测试方法,通过来测试源代码中的各个单元,例如类,方法等,以确定它们是否符合要求。直观上来说,可以将单元视为最小的可测试部分。单元测试是程序员在开发过程中创建的短代码片段。 它构成了组件测试的基础。

2 unittest

unitest是python内置的用于测试代码的模块,它支持测试自动化,配置共享和关机代码测试。支持将测试样例聚合到测试集中,并将测试与报告框架独立。

为了实现这些,unittest 通过面向对象的方式支持了一些重要的概念。

  1. 测试脚手架(test fixture):表示为了开展一项或多项测试所需要进行的准备工作,以及所有相关的清理操作。举个例子,这可能包含创建临时或代理的数据库、目录,再或者启动一个服务器进程。
  2. 测试用例(test case):一个测试用例是一个独立的测试单元。它检查输入特定的数据时的响应。 unittest 提供一个基类:TestCase ,用于新建测试用例。
  3. 测试套件(test suite): 是一系列的测试用例,或测试套件,或两者皆有。它用于归档需要一起执行的测试。
  4. 测试运行器(test runner):是一个用于执行和输出测试结果的组件。这个运行器可能使用图形接口、文本接口,或返回一个特定的值表示运行测试的结果。

2.1 创建一个简单的单元测试:

步骤

  1. 在程序中导入unittest模块。
  2. 定义一个需要测试的函数。在下面的例子中,add()函数将会被测试。
  3. 通过集成类unittest.TestCase创建一个测试用例类。
  4. 在类中定义一个测试方法,该方法以test作为前缀。
  5. 每个测试方法中都调用TestCase类的断言方法。里面有多种类型的断言。
  6. assertEquals()比较把add()函数的结果与第二个参数做比较。如果不想等,会抛出异常assertionError。
  7. 调用unittest模块中的main方法。

代码:(文件名为unittest01.py)

import unittest

def add(a, b):
return a+b # 一个类是一个测试用例,测试指类中的测试方法,测试用力中可以有多个测试。
class SimpleTest(unittest.TestCase):
def test_add(self):
self.assertEqual(add(3, 7), 10) def test_add2(self):
self.assertEqual(add(3, 5), 10) # __name__是一个全局变量,它的值是模块的名称。该判断语句表示只运行当前文件中的代码。
if __name__ == "__main__":
unittest.main()

结果

.F
======================================================================
FAIL: test_add2 (__main__.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/hookind/Desktop/workspace/pythonTest/unittest01.py", line 13, in test_add2
self.assertEqual(add(3, 5), 10)
AssertionError: 8 != 10 ----------------------------------------------------------------------
Ran 2 tests in 0.001s FAILED (failures=1)

2.2 命令行界面:

可以在命令行使用unittest模块运行模块、类和独立测试方法中的测试(这里测试指指测试用例类中的测试方法):

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

示例:对于上面的代码,可以通过下面这些命令运行。

python -m unittest unittest01
python3 -m unittest unittest01.SimpleTest
python3 -m unittest unittest01.SimpleTest.test_add

unittest支持一些命令行选项,可以通过下面的命令查看所有选项。

python3 -m unittest -h

3 API

这章只描述unittest模块中的类与方法。

3.1 TestCase类

该类的对象表示最小的可测试单元。

在TestCase类中的一些方法。

编号 方法 描述
1 setUp() 用来准备测试脚手架的方法,在调用测试方法之前,该方法会被立即调用
2 tearDown() 测试方法被调用后,该方法会被立即调用,并且结果也会被记录。即使测试方法抛出异常,该方法也会被调用
3 setUpClass() 一个类方法,在类中所有的测试运行前被调用
4 tearDownClass() 一个类方法,在类中所有的测试运行后被调用

脚手架(Fixture)

在测试用例类中,可以有大量的测试。这些方法可能需要初始化数据库连接、临时文件或其它的资源。这些就是脚手架。测试用力需要一些特别的方法配置和清理测试需要的脚手架。为了配置脚手架,覆盖setUp()方法;为了清理,覆盖tearDown()方法。

类脚手架(Class Fixture)

TestCase类有一个setUpClass()方法,该方法可以在TestCase类中的测试执行之前被覆盖以执行。同样,tearDownClass() 方法将在类中的所有测试之后执行。这两种方法都是类方法。因此,它们必须@classmethod注解装饰。

示例:

import unittest

def add(a, b):
return a+b class SimpleTest(unittest.TestCase): @classmethod
def setUpClass(cls) -> None:
print("==========setUpClass=========")
return super().setUpClass() @classmethod
def tearDownClass(cls) -> None:
print("\n==========tearDownClass=========")
return super().tearDownClass() def setUp(self):
print("\n==========setUp=========")
return super().setUp() def tearDown(self):
print("==========tearDown========")
return super().tearDown() def test_add(self):
print("\n执行test_add")
self.assertEqual(add(3, 7), 10) def test_add2(self):
print("\n执行test_add2")
self.assertEqual(add(3, 5), 10) # __name__是一个全局变量,它的值是模块的名称。该判断语句表示只运行当前文件中的代码。
if __name__ == "__main__":
unittest.main()

结果

==========setUpClass=========

==========setUp=========

执行test_add
==========tearDown========
.
==========setUp========= 执行test_add2
==========tearDown========
F
==========tearDownClass========= ======================================================================
FAIL: test_add2 (__main__.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/hookind/Desktop/workspace/pythonTest/unittest01.py", line 34, in test_add2
self.assertEqual(add(3, 5), 10)
AssertionError: 8 != 10 ----------------------------------------------------------------------
Ran 2 tests in 0.001s FAILED (failures=1)

想了解更多,请参考unittest.TestCase

3.2 TestSuite类

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

步骤

  1. 创建一个TestSuite类的实例。
suite = unittest.TestSuite()
  1. 把测试(测试用例中的一个测试方法)加入TestSuite实例中。
# 添加测试用例类中的一个测试
suite.addTest(SimpleTest2("test_sub"))
# 添加测试用例类中的所有测试
suite.addTest(unittest.makeSuite(SimpleTest))
  1. 创建一个TextTestRunner类的对象。
runner = unittest.TextTestRunner()
  1. 调用run()方法去运行在套件中的测试。
runner.run(suite)

示例

import unittest

def add(a, b):
return a+b def sub(a, b):
return a-b class SimpleTest(unittest.TestCase): @classmethod
def setUpClass(cls) -> None:
print("==========setUpClass=========")
return super().setUpClass() @classmethod
def tearDownClass(cls) -> None:
print("\n==========tearDownClass=========")
return super().tearDownClass() def setUp(self):
print("\n==========setUp=========")
return super().setUp() def tearDown(self):
print("==========tearDown========")
return super().tearDown() def test_add(self):
print("\n执行test_add")
self.assertEqual(add(3, 7), 10) def test_add2(self):
print("\n执行test_add2")
self.assertEqual(add(3, 5), 10) class SimpleTest2(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("==========setUpClass=========")
return super().setUpClass() @classmethod
def tearDownClass(cls) -> None:
print("\n==========tearDownClass=========")
return super().tearDownClass() def setUp(self):
print("\n==========setUp=========")
return super().setUp() def tearDown(self):
print("==========tearDown========")
return super().tearDown()
def test_sub(self):
print("\n执行test_sub")
self.assertEqual(sub(3, 5), 10) def test_sub2(self):
print("\n执行test_sub2")
self.assertEqual(sub(3, 5), -2) # __name__是一个全局变量,它的值是模块的名称。该判断语句表示只运行当前文件中的代码。
if __name__ == "__main__":
suite = unittest.TestSuite()
suite.addTest(SimpleTest2("test_sub"))
suite.addTest(unittest.makeSuite(SimpleTest))
runner = unittest.TextTestRunner()
runner.run(suite)

结果

==========setUpClass=========

==========setUp=========

执行test_sub
==========tearDown========
F
==========tearDownClass=========
==========setUpClass========= ==========setUp========= 执行test_add
==========tearDown========
.
==========setUp========= 执行test_add2
==========tearDown========
F
==========tearDownClass========= ======================================================================
FAIL: test_sub (__main__.SimpleTest2)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/hookind/Desktop/workspace/pythonTest/unittest01.py", line 61, in test_sub
self.assertEqual(sub(3, 5), 10)
AssertionError: -2 != 10 ======================================================================
FAIL: test_add2 (__main__.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/hookind/Desktop/workspace/pythonTest/unittest01.py", line 38, in test_add2
self.assertEqual(add(3, 5), 10)
AssertionError: 8 != 10 ----------------------------------------------------------------------
Ran 3 tests in 0.001s FAILED (failures=2)

想了解更多,可参考unittest.TestSuite

3.3 TestLoader类

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

示例

import unittest

def add(a, b):
return a+b def sub(a, b):
return a-b class SimpleTest(unittest.TestCase): @classmethod
def setUpClass(cls) -> None:
print("==========setUpClass=========")
return super().setUpClass() @classmethod
def tearDownClass(cls) -> None:
print("\n==========tearDownClass=========")
return super().tearDownClass() def setUp(self):
print("\n==========setUp=========")
return super().setUp() def tearDown(self):
print("==========tearDown========")
return super().tearDown() def test_add(self):
print("\n执行test_add")
self.assertEqual(add(3, 7), 10) def test_add2(self):
print("\n执行test_add2")
self.assertEqual(add(3, 5), 10) class SimpleTest2(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
print("==========setUpClass=========")
return super().setUpClass() @classmethod
def tearDownClass(cls) -> None:
print("\n==========tearDownClass=========")
return super().tearDownClass() def setUp(self):
print("\n==========setUp=========")
return super().setUp() def tearDown(self):
print("==========tearDown========")
return super().tearDown()
def test_sub(self):
print("\n执行test_sub")
self.assertEqual(sub(3, 5), 10) def test_sub2(self):
print("\n执行test_sub2")
self.assertEqual(sub(3, 5), -2) # __name__是一个全局变量,它的值是模块的名称。该判断语句表示只运行当前文件中的代码。
if __name__ == "__main__":
testCaseList = [SimpleTest, SimpleTest2]
testLoad = unittest.TestLoader()
testSuiteList=[]
for testCase in testCaseList:
testSuite = testLoad.loadTestsFromTestCase(testCase)
testSuiteList.append(testSuite)
newSuite = unittest.TestSuite(testSuiteList)
runner = unittest.TextTestRunner()
runner.run(newSuite)

想了解更多,可参考unittest.TestLoader

3.4 TestResult类

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

可以在上面代码中使用下述代码获得TestResult对象。

result = runner.run(newSuite)

更多请参考unittest.TestResult

4 断言

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

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

  1. 基本布尔断言
  2. 比较断言
  3. 集合断言

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

示例

import unittest

class TestCase01(unittest.TestCase):

    def test_equal(self):
self.assertEqual(12,13,"不相等")
def test_equal2(self):
self.assertEqual(12,13)
def test_equal3(self):
self.assertEqual(12,12) if __name__=="__main__":
unittest.main()

** 结果**:

FF.
======================================================================
FAIL: test_equal (__main__.TestCase01)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/hookind/Desktop/workspace/pythonTest/unittest03.py", line 6, in test_equal
self.assertEqual(12,13,"不相等")
AssertionError: 12 != 13 : 不相等 ======================================================================
FAIL: test_equal2 (__main__.TestCase01)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/hookind/Desktop/workspace/pythonTest/unittest03.py", line 8, in test_equal2
self.assertEqual(12,13)
AssertionError: 12 != 13 ----------------------------------------------------------------------
Ran 3 tests in 0.000s FAILED (failures=2)

想了解更多,请参考assert-methods

5 参考

  1. UnitTest Framework Tutorial
  2. unittest — Unit testing framework

Python Unittest简明教程的更多相关文章

  1. 飘逸的python - yield简明教程

    发现还有非常多人对yield不理解,云里雾里,于是试着用文字表述. 仅仅要函数含有yield语句,它就返回一个生成器.所以我们与其把其看成函数定义,不如看作是生成器定义.函数用return返回,而生成 ...

  2. 《Python简明教程》总结

    Python经典教程<Python简明教程> 目录: 为什么Python 安装Python 体验Python Python数据类型 运算符与表达式 控制流 函数 模块 数据结构 解决问题 ...

  3. 【笔记】Python简明教程

    Python简明教程,此资源位于http://woodpecker.org.cn/abyteofpython_cn/chinese/ s=u'中文字符' #u表示unicode,使用u之后能正常显示中 ...

  4. 2017-2018-2 20179207 《网络攻防技术》python简明教程(1-10)

    Python3简明教程(一) 开始python之旅 使用交互模式的 Python3解释器 简单使用 vim 编写 Python3 脚本 执行 Python3 脚本 Python3 代码风格建议 Pyt ...

  5. python中global的用法——再读python简明教程

    今天看了知乎@萧井陌的编程入门指南,想重温一下 <python简明教程>,对global的用法一直不太熟练,在此熟练一下,并实践一下python中list.tuple.set作为参数的区别 ...

  6. python简明教程

    Python简明教程 MachinePlay关注 0.7072018.09.26 01:49:43字数 2,805阅读 9,287 Python一小时快速入门 1.Python简介   pylogo. ...

  7. Python的入门级试用(简明教程)

    声明:借鉴Python 简明教程 用 Python 编写的传统的 'Hello World' 程序.使用 Python 运行你的程序的方法有两种:使用交互式解释器提示符或者使用源文件.现在我们来看一下 ...

  8. Python 简明教程 --- 3,Python 基础概念

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 控制复杂性是计算机编程的本质. -- Brian Kernighan 了解了如何编写第一个Pytho ...

  9. Python 简明教程 --- 2,第一个Python 程序

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 如果你发现特殊情况太多,那你肯定是用错方法了. -- Carig Zerouni 当你在自己的电脑上 ...

随机推荐

  1. jvm相关自我总结和 VisualVM工具的使用

    idea 二个工具: jclasslib Hexview jdk监控工具 VisualVM工具的使用: https://www.ibm.com/developerworks/cn/java/j-lo- ...

  2. B-Tree插入和删除的Java实现

    B-Tree插入和删除的Java实现 一.一颗非空m阶B-Tree的性质 除根结点以外的每个结点的孩子引用最多存在m个,关键码最多存在m - 1个:除根结点以外的每个结点的孩子引用至少存在⌈m / 2 ...

  3. 从实力的角度出发来思考这道AOP题目

    文/楠木大叔 技术更迭,一往无前.技术人总是要不断学习以适应社会的发展和行业对我们的要求.每隔一段时间,就会有纷至沓来的新技术,新知识,新概念,我们应该如何应对,是被逼到墙角,还是主动出击? 导读 从 ...

  4. 量子:基于ERP块对的两步量子直接通信

    学习论文: 题目:Two-step quantum direct communication protocol using the Einstein-Podolsky-Rosen pair block ...

  5. 【NX二次开发】Block UI 指定矢量

    属性说明 属性   类型   描述   常规           BlockID    String    控件ID    Enable    Logical    是否可操作    Group    ...

  6. Django基础之路由层

    内容概要 路由匹配 无名有名分组 反向解析 无名有名分组反向解析(难理解) 路由分发 名称空间 伪静态 内容详细 1 路由匹配 urls.py url()方法第一个参数其实是一个正则表达式 第一个参数 ...

  7. CLR里的MethodTable,MethodDescChunk,MethodDesc,FixUpPreCode都是什么意思

    一:看下面一些概念 1MethodTable MethodTable可以说在CLR里面无处不在,这个东西主要是作为对象的数据类型存在,主要包含了EEClass 模块地址,类型名称,模块路径等. 2.E ...

  8. PTA题目集4-6总结

    PTA题目集4-6总结 一:前言 在题集4-6中,所考查的主要知识点有正则表达式,类与类之间的调用,类的聚合,继承,封装,接口与多态,三种排序方法如选择排序,冒泡排序,插入排序,ArrayList,s ...

  9. Webflux请求处理流程

    spring mvc处理流程 在了解SpringMvc的请求流程源码之后,理解WebFlux就容易的多,毕竟WebFlux处理流程是模仿Servlet另起炉灶的. 下面是spring mvc的请求处理 ...

  10. Visual Studio 2010下ASPX页面的TreeView控件循环遍历

    如果维护一个老系统就总会遇到各种问题,而这次是TreeView的循环遍历.对于Visual Studio2010上aspx页面的TreeView控件,我感受到了什么叫集微软之大智慧.与二叉树型不一样. ...