单元测试的重要性就不多说了,可恶的是python中有太多的单元测试框架和工具,什么unittest, testtools, subunit, coverage, testrepository, nose, mox, mock, fixtures, discover,再加上setuptools, distutils等等这些,先不说如何写单元测试,光是怎么运行单元测试就有N多种方法,再因为它是测试而非功能,是很多人没兴趣触及的东西。但是作为一个优秀的程序员,不仅要写好功能代码,写好测试代码一样的彰显你的实力。如此多的框架和工具,很容易让人困惑,困惑的原因是因为并没有理解它的基本原理,如果一些基本的概念都不清楚,怎么能够写出思路清晰的测试代码?

今天的主题就是unittest,作为标准python中的一个模块,是其它框架和工具的基础,参考资料是它的官方文档:http://docs.python.org/2.7/library/unittest.html和源代码,文档已经写的非常好了,我在这里记录的主要是它的一些重要概念、关键点以及可能会碰到的一些坑,目的在于对unittest加深理解,而不是停留在泛泛的表面层上。

unittest是一个python版本的junit,junit是java中的单元测试框架,对java的单元测试,有一句话很贴切:Keep the bar green,相信使用eclipse写过java单元测试的都心领神会。unittest实现了很多junit中的概念,比如我们非常熟悉的test case, test suite等,总之,原理都是相通的,只是用不同的语言表达出来。

在文档的开篇就介绍了unittest中的4个重要的概念:test fixture, test case, test suite, test runner,我觉得只有理解了这几个概念,才能真正的理解单元测试的基本原理,下面就主要围绕这几个概念来展开这篇文章。

首先通过查看unittest的源码,来看一下这几个概念,以及他们之间的关系,他们是如何在一起工作的,其静态类图如下:

  • 一个TestCase的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。元测试(unit test)的本质也就在这里,一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。
  • 而多个测试用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。
  • TestLoader是用来加载TestCase到TestSuite中的,其中有几个loadTestsFrom__()方法,就是从各个地方寻找TestCase,创建它们的实例,然后add到TestSuite中,再返回一个TestSuite实例。
  • TextTestRunner是来执行测试用例的,其中的run(test)会执行TestSuite/TestCase中的run(result)方法。
  • 测试的结果会保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。

这样整个流程就清楚了,首先是要写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,整个过程集成在unittest.main模块中。

现在已经涉及到了test case, test suite, test runner这三个概念了,还有test fixture没有提到,那什么是test fixture呢??在TestCase的docstring中有这样一段话:

Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.

可见,对一个测试用例环境的搭建和销毁,是一个fixture,通过覆盖TestCase的setUp()和tearDown()方法来实现。这个有什么用呢?比如说在这个测试用例中需要访问数据库,那么可以在setUp()中建立数据库连接以及进行一些初始化,在tearDown()中清除在数据库中产生的数据,然后关闭连接。注意tearDown的过程很重要,要为以后的TestCase留下一个干净的环境。关于fixture,还有一个专门的库函数叫做fixtures,功能更加强大,以后会介绍到。

至此,概念和流程基本清楚了,下面通过简单的例子再来实践一下,就拿unittest文档上的例子吧:

import random
import unittest class TestSequenceFunctions(unittest.TestCase): def setUp(self):
self.seq = range(10) def test_shuffle(self):
# make sure the shuffled sequence does not lose any elements
random.shuffle(self.seq)
self.seq.sort()
self.assertEqual(self.seq, range(10)) # should raise an exception for an immutable sequence
self.assertRaises(TypeError, random.shuffle, (1,2,3)) def test_choice(self):
element = random.choice(self.seq)
self.assertTrue(element in self.seq) def test_sample(self):
with self.assertRaises(ValueError):
random.sample(self.seq, 20)
for element in random.sample(self.seq, 5):
self.assertTrue(element in self.seq) if __name__ == '__main__':
unittest.main()

TestSequenceFunctions继承自unittest.TestCase,重写了setUp()方法,并且定义了三个以'test'开头的方法,那这个TestSequenceFunctions类到底是个什么呢?它是一个测试用例,还是三个测试用例?说是三个测试用例的话,它本身继承自TestCase,说是一个测试用例的话,里面又有三个test_*()方法,明显是三个测试用例。其实,我们只要看一些TestLoader是如何加载测试用例的,就一清二楚了,在loader.TestLoader类中有一个loadTestsFromTestCase()方法:

    def loadTestsFromTestCase(self, testCaseClass):
"""Return a suite of all tests cases contained in testCaseClass"""
if issubclass(testCaseClass, suite.TestSuite):
raise TypeError("Test cases should not be derived from TestSuite." \
" Maybe you meant to derive from TestCase?")
testCaseNames = self.getTestCaseNames(testCaseClass)
if not testCaseNames and hasattr(testCaseClass, 'runTest'):
testCaseNames = ['runTest']
loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
return loaded_suite

getTestCaseNames()是从TestCase这个类中找所有以“test”开头的方法,然后注意第9行,在构造TestSuite对象时,其参数使用了一个map方法,即对testCaseNames中的每一个元素,使用testCaseClass为其构造对象,其结果是一个TestCase的对象集合,可以用下面的代码来分步说明:

testcases = []
for name in testCaeNames:
testcases.append(TestCase(name))
loaded_suite = self.suiteClass(tuple(testcases))

可见,对每一个以test开头的方法,都为其构建了一个TestCase对象,值得注意的是,如果没有定义test开头的方法,而是将测试代码写到了一个名为runTest的方法中,那么会为该runTest方法构建TestCase对象,如果定义了test开头的方法,就会忽略runTest方法。

至此,基本就清楚了,每一个以test开头的方法,都会为其构建TestCase对象,也就是说TestSequenceFunctions类中其实定义了三个TestCase,之所以写成这样,是为了方便,因为这几个测试用例的fixture是相同的,如果每一个测试用例单独写成一个TestCase的话,会有很多的冗余代码。

明白了这些,文档就可以很轻松的看懂了,至于怎么运行测试用例,以及其他的内容,直接看文档吧。

Python单元测试——深入理解unittest的更多相关文章

  1. Python单元测试——深入理解unittest (转)

    单元测试的重要性就不多说了,可恶的是Python中 有太多的单元测试框架和工具,什么unittest, testtools, subunit, coverage, testrepository, no ...

  2. python单元测试--深入理解unittest

    单元测试的重要性就不多说了,可恶的是python中有太多的单元测试框架和工具,什么unittest, testtools, subunit, coverage, testrepository, nos ...

  3. Python单元测试框架:unittest(一)

    Python单元测试框架unittest使用方法讲解 主要介绍了Python单元测试框架unittest使用方法讲解,本文讲解了unittest概述.命令行接口.测试案例自动搜索.创建测试代码.构建测 ...

  4. Python单元测试框架:unittest(二)

    一.直接使用TestCase 注意所有测试方法都需要以test开头.代码如下: import unittest class Test1(unittest.TestCase): @classmethod ...

  5. Python之自动单元测试之一(unittest使用实例)

    软件的测试是一件非常乏味的事情,在测试别人编写的软件时尤其如此,程序员通常都只对编写代码感兴趣,而不喜欢文档编写和软件测试这类"没有创新"的工作.既然如此,为什么不让程序员在编写软 ...

  6. python单元测试unittest

    单元测试作为任何语言的开发者都应该是必要的,因为时隔数月后再回来调试自己的复杂程序时,其实也是很崩溃的事情.虽然会很快熟悉内容,但是修改和 调试将是一件痛苦的事情,如果你在修改了代码后出现问题的话,而 ...

  7. Python单元测试:unittest使用简介

    一.概述 本文介绍python的单元测试框架unittest,这是Python自带的标准模块unittest.unittest是基于java中的流行单元测试框架junit设计的,其功能强大且灵活,对于 ...

  8. [转]python单元测试unittest

    单元测试作为任何语言的开发者都应该是必要的,因为时隔数月后再回来调试自己的复杂程序时,其实也是很崩溃的事情.虽然会很快熟悉内容,但是修改和调试将是一件痛苦的事情,如果你在修改了代码后出现问题的话,而单 ...

  9. 【python接口自动化框架-unittest】【一】unittest单元测试框架概念

    一.unittst单元测试框架 概念参考:https://docs.python.org/2/library/unittest.html 使用方法:import unittest (引入unittes ...

随机推荐

  1. mysql管理---表分区

    一.什么是表分区 通俗地讲表分区是将一大表,根据条件分割成若干个小表.mysql5.1开始支持数据表分区了. 如:某用户表的记录超过了600万条,那么就可以根据入库日期将表分区,也可以根据所在地将表分 ...

  2. 【bzoj1552】[Cerc2007]robotic sort

    题目描述 输入 输入共两行,第一行为一个整数N,N表示物品的个数,1<=N<=100000.第二行为N个用空格隔开的正整数,表示N个物品最初排列的编号. 输出 输出共一行,N个用空格隔开的 ...

  3. Unknown/unsupported storage engine: InnoDB

    症状:无法启动mysql,在“mysql数据库目录/主机名.err”日志文件中报错 Unknown/unsupported storage engine: InnoDB原因:MySQL5.5.8 GA ...

  4. Windows录音API学习笔记(转)

    源:Windows录音API学习笔记 Windows录音API学习笔记 结构体和函数信息  结构体 WAVEINCAPS 该结构描述了一个波形音频输入设备的能力. typedef struct { W ...

  5. eclipse中集成svn maven开发手册---创建提分支

    开发时,需要拉取分支进行修改等操作 右键项目,选择team->分支/标记 输入创建分支地址 注意: 1,创建分支路径时,最后一层文件名称为项目的名称 2,点击浏览按钮可能会出现无法选择,且ecl ...

  6. App IM 之 环信

    文档参考:http://docs.easemob.com/docs.php 开发社区:http://www.imgeek.org 也可以在官网页面上点击客服进行咨询 1. 环信 之 文件导航 2. 环 ...

  7. JDBC连接sql server数据库的详细步骤和代码

    JDBC连接sql server数据库的详细步骤和代码 JDBC连接sql server数据库的步骤如下: 1.加载JDBC驱动程序: 在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Ja ...

  8. AutoLayout没有相对比例布局

    怎么实现相对比例布局 比如我一个控件相对上边距的位置在整个屏幕的比例 可以用stack view来管理相对布局

  9. 排序问题思考(要求时间和空间复杂度尽可能的低)【Part 2】

    继上篇博文,今天我将先介绍一下什么是计数排序,将计数排序描述清楚后,再进行后续的桶排序方法解决这个问题. 通常情况下,一提到排序,大家第一反应就是比较,其实,今天我要说的这个计数排序,不是基于比较的排 ...

  10. Linux下的文件目录结构详解

    Linux下的文件目录结构详解 / Linux文件系统的上层根目录 /bin 存放用户可执行的程序 /boot 操作系统启动时所需要的文件 /dev 接口设备文件目录,例如:had表示硬盘 /etc ...