Python单元测试框架 unittest详解
一 整体结构概览
unittest原名为PyUnit,是由java的JUnit衍生而来。对于单元测试,需要设置预先条件,对比预期结果和实际结果。
- TestCase :通过继承TestCase类,我们可以创建一个test,或者一组tests. 一个TestCase的实例就是一个测试用例,是一个完整的测试流程,包括测试前准备环境的搭建(setUp),实现测试过程的代码(run),测试后环境的还原(tearDown).
- Test Suites : 测试套件, 把多个测试用例集合在一起来执行。可以通过addTest加载TestCase到Test Suite中,从而返回一个TestSuite实例。
- Test Fixtures : setup + test case + teardown结构 , 对一个测试用例环境的搭建和销毁。通过覆盖TestCase的setUp()和tearDown()方法来实现。tearDown()为下一个测试用例提供一个干净的环境。
- Test Runner : 测试的执行,通过TextTestRunner类提供的run()方法来执行Test Suite/TestCase。Test Runner可以使用图形界面,文本界面,或者返回一个特殊的值的方式来表示测试执行的结果。

所有的测试函数以test开头,test_XXX。
# -*-coding:utf--*-
import unittest # 被测试的函数,姓名格式化输出
def get_formatted_name(first,last):
"""Generate a neatly formatted full name."""
full_name = first + " " + last
return full_name.title() class NameTestCase(unittest.TestCase):
# 从unitteset的包中继承TestCase这个类,这样Python能够识别你编写的测试
def test_first_last_name(self):
"""
测试用例:所有以test开头的
"""
formatted_name = get_formatted_name("jian","yu")
self.assertEqual(formatted_name,"Jia Yu") if __name__ == "__main__":
unittest.main()
如果被测试的函数(测试用例)本身有错误
会报E

如果 测试用例本身没错 而在判断比对 self.assertEqual 的时候 不一致 则会报F
以上是函数的测试,类的测试与函数的测试及其相似
import unittest #-*-coding:utf--*-
class AnonymousSurvey():
"""收集匿名调查问卷答案"""
def __init__(self,question=None):
"""
Args:
question:restore the answers for question
Return:
None
"""
self.question = question
self.responses = [] def show_question(self):
"""
print question
"""
print(self.question) def store_response(self,new_response):
self.responses.append(new_response) def show_results(self):
"""
显示收集到的所有答案
"""
print("Survey Results")
for response in self.responses:
print("-"+response) class TestAnonymousSurvey(unittest.TestCase):
"""
A test according to AnonymousSurvey
"""
def test_store_single_response(self):
question = "what lauguage did you first learn to speak?"
my_survey = AnonymousSurvey(question)
my_survey.store_response("Chinese")
self.assertIn("Chinese",my_survey.responses) def test_store_multi_responses(self):
question = "what lauguage did you first learn to speak?"
my_survey = AnonymousSurvey(question)
responses = ["Chinese","English","French","German"]
for response in responses:
my_survey.store_response(response)
for response in responses:
self.assertIn(response,my_survey.responses) if __name__ == "__main__":
unittest.main()
这里可以看到 两个测试样例有一定的重复部分,可以利用unittest.TestCase类方法setUp(),作为共享变量初始化,Python运行TestCase的类会首先运行setUp() (相当于unittest.TestCase版的def __init__())
#-*-coding:utf--*-
import unittest
class AnonymousSurvey():
"""收集匿名调查问卷答案"""
def __init__(self,question=None):
"""
Args:
question:restore the answers for question
Return:
None
"""
self.question = question
self.responses = [] def show_question(self):
"""
print question
"""
print(self.question) def store_response(self,new_response):
self.responses.append(new_response) def show_results(self):
"""
显示收集到的所有答案
"""
print("Survey Results")
for response in self.responses:
print("-"+response) class TestAnonymousSurvey(unittest.TestCase):
"""
A test according to AnonymousSurvey
"""
def setUp(self):
question = "what lauguage did you first learn to speak?"
self.my_survey = AnonymousSurvey(question)
self.responses = ["Chinese","English","French","German"] def test_store_single_response(self):
self.my_survey.store_response("Chinese")
self.assertIn("Chinese",self.my_survey.responses) def test_store_multi_responses(self):
for response in self.responses:
self.my_survey.store_response(response)
for response in self.responses:
self.assertIn(response,self.my_survey.responses) if __name__ == "__main__":
unittest.main()
但值得注意的时 setUp()内的my_survey和responses都需要增加前缀self(意为储存在类属性中),
因此可在这个类的任何地方使用
否则无法传递
二 命令行
- 从命令行中可以运行单元测试的模块,类,甚至单独的测试方法。
python -m unittest test_module1 test_module2
#同时测试多个module
python -m unittest test_module.TestClass
python -m unittest test_module.TestClass.test_method
- 显示更详细的测试结果的说明使用 [-v] flag:
python -m unittest -v test_module
- 查看所有的命令行选项使用命令
python -m unittest -h
三 TestCase
- Testcase类 <形如class xxxTestCase(unittest.TestCase) >
看下面的例子(创建一个测试类DefaultWidgetSizeTestCase):
import unittest
class DefaultWidgetSizeTestCase(unittest.TestCase):
#unittest.TestCase表示某个测试函数
def runTest(self):
widget = Widget('The widget')
self.assertEqual(widget.size(), (, ), 'incorrect default size')
创建实例建立这样一个测试用例的一个实例,使用该类的构造函数,且不带参数(这样会执行所有的测试方法):
testCase = DefaultWidgetSizeTestCase()
建了两个WidgetTestCase的实例,每个实例只运行WidgetTestCase类中的一个测试方法(通过参数传入)
defaultSizeTestCase = WidgetTestCase('test_default_size')
resizeTestCase = WidgetTestCase('test_resize')
常用断言方法 <查看官方文档>
unittest库提供了很多实用方法来检测程序运行的结果和预期。包括三种类型的方法,每一种都覆盖了典型的类型
- 检测元素是否相等:assertEqual(a,b [,msg]):
assertEqual(element.text, "")
assertNotEqual(a,b [,smg]):检测a!==b.
- 检测表达式是否为Ture,或者 False:
assertTrue(x [,msg])
assertFalse(x [,msg])
- 检测异常assertRaises(exc, fun, *args, **kwds)
assertRaiseRegexp(exc, r, fun, *args, **kwds)
- 逻辑运算
assertGreater(a, b) # 检测a > b.
assertGreaterEqual(a ,b) # 检测a >= b.
assertLess(a, b) #检测a < b.
assertLessEqual(a, b) #检测a <= b.="" <="" code=""></=>
- 正则表达式,检测正则是否匹配给定的text
assertRegexpMatches(s, r) #检测r.search(s).
assertNotRegexpMatches(s, r) #检测not r.search(s).
四 Test fixtures
方法固定装置:
如果要对一个模块中的每一个测试函数都做同样的初始化操作和结尾清除等操作,那么创建n个测试用例就得写n遍一样的代码,为了减少重复的代码,可以使用下面两个函数:
setUp(): 每次执行测试用例之前调用。无参数,无返回值。该方法抛出的异常都视为error,而不是测试不通过。没有默认的实现。
tearDown(): 每次执行测试用例之后调用。无参数,无返回值。测试方法抛出异常,该方法也正常调用,该方法抛出的异常都视为error,而不是测试不通过。只用setUp()调用成功,该方法才会被调用。没有默认的实现。通过setup 和 tesrDown组装一个module成为一个固定的测试装置。注意:如果setup运行抛出错误,则测试用例代码则不会执行。但是,如果setpu执行成功,不管测试用例是否执行成功都会执行teardown。
Class固定装置:
必须为类实现
setUpClass():一个类方法在单个类测试之前运行。setUpClass作为唯一的参数被调用时,必须使用classmethod()作为装饰器。
tearDownClass():一个类方法在单个类测试之后运行。setUpClass作为唯一的参数被调用时,必须使用classmethod()作为装饰器。
import unittest
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls): #这里的cls是当前类的对象
cls._connection = createExpensiveConnectionObject()
@classmethod
def tearDownClass(cls):
cls._connection.destroy()
五 使用Text Suite组织测试代码
unittest.TestSuite(tests=())
该类聚合测试用例和测试套件,运行一个TestSuite实例遍历套件,和单独运行每个testcase是相同的。TestSuite对象的行为就像TestCase对象,除了他们不实现一个测试。
一些方法可以将testcase添加到TestSuite实例:
addTest(test):Add a TestCase or TestSuite to the suite.
addTests(tests):添加所有的tests从可迭代的TestCase和TestSuite实例测试套件。这相当于迭代调用addTest()来添加每个元素。
根据不同的业务可能需要在不同的module中选择某一个或者几个测试用例,此时可以根据每个测试实例的特征对测试方法打包:
widgetTestSuite = unittest.TestSuite()
#创建一个测试套件实例
widgetTestSuite.addTest(WidgetTestCase('test_default_size'))
#添加测试用例到套件,抽取WidgetTestCase类中的test_default_size测试用例添加到
testsuitewidgetTestSuite.addTest(WidgetTestCase('test_resize'))
#添加测试用例到套件,抽取WidgetTestCase类中的test_resize测试用例添加到testsuite
可以返回该测试套件的get入口:
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase('test_default_size'))
suite.addTest(WidgetTestCase('test_resize'))
return suite
或者更简洁的写法:
def suite():
tests = ['test_default_size', 'test_resize']
return unittest.TestSuite(map(WidgetTestCase, tests))
测试套件中也可以包含测试套件:
suite1 = module1.TheTestSuite()
suite2 = module2.TheTestSuite()
alltests = unittest.TestSuite([suite1, suite2])
使用TestLoader
lass unittest.TestLoader
TestLoader 用来从clases和modules创建test suites,通常也需要创建一个该类的实例,unittest模块提供了一个实例,可以作为unittest.defaultTestLoader共享。使用一个子类或实例,允许定制可配置属性。该类有以下方法:loadTestsFromTestCase(testCaseClass):
loadTestsFromModule(module):返回一个给定的模块中所有测试用例,打包成一个套件返回。该类创建一个testsuites然后加载一个module并执行其中所有的测试用例,执行的顺序是根据测试用例的名称来的。
suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase)
#执行WidgetTestCase中所有的测试用例
你可以将测试用例和测试套件放在一个module中,最好是分开放置,方便重构管理,如果测试策略改变了,也方便维护。
六 跳过测试和预期的失败
Unittest支持跳过单个的测试方法甚至整个类的测试。使用 skip() decorator来设置特定跳过的条件,如指定操作系统不执行该测试。
执行的时候如果满足跳过条件,控制台会将后面的说明打印出来,并跳过该测试用例。跳过类也是相似的写法。也可以自定义skipping装饰器。
定义预期的失败使用unittest.expectedFailure(),运行时 ,如果测试失败,测试不算作失败。
class TestDtuOp(unittest.TestCase):
def setUp(self):
print("\n=======================================") def tearDown(self):
pass @tt.show_test_case_name
def test_upper(self):
""" This test should be passed. """
self.assertEqual('foo'.upper(), 'FOO') def test_error(self):
""" This test should be marked as error one. """
raise ValueError def test_fail(self):
""" This test should fail. """
self.assertEqual(1, 2) @unittest.skip("This is a skipped test.")
def test_skip(self):
""" This test should be skipped. """
pass @unittest.expectedFailure
def test_expectedFailure(self):
""" This test should be expectedFailure. """
pass
七 使用HTMLTestRunner生成报告
unittest本身并不具备这个功能,需要使用HTMLTestRunner库使用步骤 (这里暂时不进行拓展)
Python单元测试框架 unittest详解的更多相关文章
- Python单元测试框架unittest使用方法讲解
这篇文章主要介绍了Python单元测试框架unittest使用方法讲解,本文讲解了unittest概述.命令行接口.测试案例自动搜索.创建测试代码.构建测试套件方法等内容,需要的朋友可以参考下 概 ...
- Python单元测试框架unittest之深入学习
前言 前几篇文章该要地介绍了python单元测试框架unittest的使用,本篇文章系统介绍unittest框架. 一.unittest核心工作原理 unittest中最核心的四个概念是:test c ...
- Python单元测试框架unittest之单用例管理(一)
一.概述 本文介绍python的单元测试框架unittest,unittest原名为PyUnit,是由java的JUnit衍生而来,这是Python自带的标准模块unittest.unittest是基 ...
- Python单元测试框架unittest
学习接口自动化测试时接触了unittest单元测试框架,学习时参照了虫师编写的<selenium2自动化测试实战>,个人觉得里面讲的例子还比较容易理解的. 一.基础 1.main()和框架 ...
- Python单元测试框架unittest重要属性 与 用例编写思路
前言 本文为转载,原文地址作者列举python unittest这个测试框架的主要属性和 测试用例思路 unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动化测试用例的开发与执行, ...
- Python 定时任务框架 APScheduler 详解
APScheduler 最近想写个任务调度程序,于是研究了下 Python 中的任务调度工具,比较有名的是:Celery,RQ,APScheduler. Celery:非常强大的分布式任务调度框架 R ...
- python单元测试框架-unittest(一)
简介 unittest单元测试框架不仅可以适用于单元测试,还可以使用WEB自动化测试用例的开发与执行,该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果. ...
- javascript单元测试框架mochajs详解
关于单元测试的想法 对于一些比较重要的项目,每次更新代码之后总是要自己测好久,担心一旦上线出了问题影响的服务太多,此时就希望能有一个比较规范的测试流程.在github上看到牛逼的javascript开 ...
- javascript单元测试框架mochajs详解(转载)
章节目录 关于单元测试的想法 mocha单元测试框架简介 安装mocha 一个简单的例子 mocha支持的断言模块 同步代码测试 异步代码测试 promise代码测试 不建议使用箭头函数 钩子函数 钩 ...
随机推荐
- Hibernate入门(七)一对多入门案例
一对多 场景模拟:用户(一)对订单(多) 1.建表 创建客户表,字段有:客户id,客户姓名,客户性别,客户年龄,客户年纪,客户电话. 创建订单表,字段有:订单编号,明细编号,客户编号(外键) DROP ...
- 当前主流电脑的BIOS调出键
[组装电脑主板] 主板品牌 启动按键 华硕主板 F8 技嘉主板 F12 微星主板 F11 映泰主板 F9 梅捷主板 ESC或F12 七彩虹主板 ESC或F11 华擎主板 F11 斯巴达 ...
- 为链表数据结构实现iterator接口
iterator作用 为所有的数据结构提供统一的访问方式. 接口对象 接口对象一共有3个方法,next()方法.return()方法.throw()方法. next() 必填 用于for..of迭代. ...
- viewer.js 视图预览demo
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- Spring 切入点配置
有关各种拦截的切入点配置举例 (1)只对返回值为String的方法进行拦截 @Pointcut("execution (java.lang.String com.zzdr.spring.se ...
- CSS中默认被继承的属性
在CSS中,所有属性都可以被继承,只需要显式的设置属性值为inherit即可.如果不设置该属性,CSS大部分属性默认不会从父元素继承而是设置初始值(initial value),但是有一部分属性,默认 ...
- AI在汽车中的应用:实用深度学习
https://mp.weixin.qq.com/s/NIza8E5clC18eMF_4GMwDw 深度学习的“深度”层面源于输入层和输出层之间实现的隐含层数目,隐含层利用数学方法处理(筛选/卷积)各 ...
- 开源项目商业模式分析(2) - 持续维护的重要性 - Selenium和WatiN
该系列第一篇发布后收到不少反馈,包括: 第一篇里说的MonicaHQ不一定盈利 没错,但是问题在于绝大多数开源项目商业数据并没有公开,从而无法判断其具体是否盈利.难得MonicaHQ是公开的,所以才用 ...
- vue 构建项目vue-cli
1.首先得有node和npm的环境,node的下载:http://nodejs.org/download/.安装node之后,npm也自动生成了,显示版本号就意味着安装成功 2.接下来就是安装vue- ...
- Android内存优化(三)详解内存分析工具MAT
前言 在这个系列的前四篇文章中,我分别介绍了DVM.ART.内存泄漏和内存检测工具的相关知识点,这一篇我们通过一个小例子,来学习如何使用内存分析工具MAT. 1.概述 在进行内存分析时,我们可以使用M ...