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代码测试 不建议使用箭头函数 钩子函数 钩 ...
随机推荐
- java 反射模式
反射模式优化工厂类大量switch分支问题 继续上一篇工厂模式的案例,上一篇只有两个算法类(加法和减法),现在再加一个乘法 第一步: //运算类 public class Operation { pr ...
- 7.通用程序设计_EJ
第45条: 将局部变量的作用域最小化 该条目与第13条(使类和成员的可访问性最小)本质上是类似的.要使局部变量的作用域最小化,最有利的方法就是在第一次使用它的地方声明.在每个局部变量的声明处都应该包含 ...
- 继续封装个 Volley 组件
本篇文章已授权微信公众号 dasu_Android(大苏)独家发布 前面已经封装了很多常用.基础的组件了:base-module, 包括了: crash 处理 常用工具类 apk 升级处理 log 组 ...
- js 常用正则表达式
1 用户名正则 //用户名正则,4到16位(字母,数字,下划线,减号) var uPattern = /^[a-zA-Z0-9_-]{4,16}$/; //输出 true console.log(uP ...
- [待优化笔记]原生JS实现验证框架 checkFun
;(function(){ /** 验证框架 checkFun * 使用方法: * <input class="required" type="text" ...
- SAP MM ME81N PO Value Analysis报表中Net Value 为负数是怎么回事?
SAP MM ME81N PO Value Analysis报表中Net Value 为负数是怎么回事? ME81N 报表中,如下PO的net value为负数, 怎么回事? 经查这些PO都是退货采购 ...
- 转载:使用redis+flask维护动态代理池
githu源码地址:https://github.com/Germey/ProxyPool更好的代理池维护:https://github.com/Python3WebSpider/ProxyPool ...
- 章节四、3-While循环-DoWhile语句
一.while死循环 package introduction5; public class WhileDemo { public static void main(String[] args) { ...
- 章节一、1-Selenium简介
一.Selenium WebDriver介绍 1.跨平台,用web浏览器做自动化的工具. 2.可以在浏览器上运行的一个框架,用来进行界面的自动化. 3.支持多种计算机语言. 4.可以模拟真实的用户去操 ...
- (网页)HTML中INPUT type="date"标签如何赋值注意问题(转)
现在的html5 input标签支持type="date" 显示有日期的日历控件,一行简单的代码就能显示出一个日历控件,但是有的时候需要给它一个默认的日期值,这个时候可能就要用到v ...