Python unittest(PyUnit)单元测试框架
PyUnit(unittest) 是 Python 自带的单元测试框架,用于编写和运行可重复的测试。PyUnit 是 xUnit 体系的一个成员,xUnit 是众多测试框架的总称,PyUnit 主要用于进行白盒测试和回归测试。
如果你使用的是 2.1 或更早版本的 Python,则可能需要自行下载和安装 PyUnit,现在的开发者通常不需要操心这些事情。
通过 PyUnit 可以让测试具有持久性,测试与开发同步进行,测试代码与开发代码一同发布。使用 PyUnit 具有如下好处:
- 可以使测试代码与产品代码分离。
- 针对某一个类的测试代码只需要进行较少的改动,便可以应用于另一个类的测试。
- PyUnit 开放源代码,可以进行二次开发,方便对 PyUnit 的扩展。
PyUnit 是一个简单、易用的测试框架,其具有如下特征:
- 使用断言方法判断期望值和实际值的差异,返回 bool 值。
- 测试驱动设备可使用共同的初始化变量或实例。
- 测试包结构便于组织和集成运行。
PyUnit (unittest) 的用法
所有测试的本质其实都是一样的,都是通过给定参数来执行函数,然后判断函数的实际输出结果和期望输出结果是否一致。
PyUnit 测试与其他 xUnit 的套路一样,基于断言机制来判断函数或方法的实际输出结果和期望输出结果是否一致,测试用例提供参数来执行函数或方法,获取它们的执行结果,然后使用断言方法来判断该函数或方法的输出结果与期望输出结果是否一致,如果一致则说明测试通过;如果不一致则说明测试不通过。
目前还有一种流行的开发方式叫作测试驱动开发,这种方式强调先编写测试用例,然后再编写函数和方法。假如程序要开发满足 A 功能的 fun_a() 函数,采用测试驱动开发的步骤如下:
- 为 fun_a() 函数编写测试用例,根据业务要求,使用大量不同的参数组合来执行 fun_a() 函数,并断言该函数的执行结果与业务期望的执行结果匹配。
- 编写、修改 fun_a() 函数。
- 运行 fun_a() 函数的测试用例,如果测试用例不能完全通过;则重复第 2 步和第 3 步,直到 fun_a() 的所有测试用例全部通过。
测试驱动开发强调结果导向,也就是在开发某个功能之前,先定义好该功能的最终结果(测试用例关注函数的执行结果),然后再去开发该功能。就像建筑工人在砌墙之前,要先拉好一根笔直的绳子(作用相当于测试用例),然后再开始砌墙,这样砌出来的墙就会符合标准。所以说测试驱动开发确实是一种不错的开发方式。
下面开发一个简单的 fk_math.py 程序,该程序包含两个函数,分别用于计算一元一次方程的解和二元一次方程的解。
- def one_equation(a , b):
- '''
- 求一元一次方程a * x + b = 0的解
- 参数a - 方程中变量的系数
- 参数b - 方程中的常量
- 返回 方程的解
- '''
- # 如果a = 0,则方程无法求解
- if a == 0:
- raise ValueError("参数错误")
- # 返回方程的解
- else:
- # return -b / a # ①
- return b / a
- def two_equation(a , b , c):
- '''
- 求一元二次方程a * x * x + b * x + c = 0的解
- 参数a - 方程中变量二次幂的系数
- 参数b - 方程中变量的系数
- 参数c - 方程中的常量
- 返回 方程的根
- '''
- # 如果a == 0,变成一元一次方程
- if a == 0:
- raise ValueError("参数错误")
- # 有理数范围内无解
- elif b * b - 4 * a * c < 0:
- raise ValueError("方程在有理数范围内无解")
- # 方程有唯一的解
- elif b * b - 4 * a * c == 0:
- # 使用数组返回方程的解
- return -b / (2 * a)
- # 方程有两个解
- else:
- r1 = (-b + (b * b - 4 * a * c) ** 0.5) / 2 / a
- r2 = (-b - (b * b - 4 * a * c) ** 0.5) / 2 / a
- # 方程的两个解
- return r1, r2
在定义好上面的 tk_math.py 程序之后,该程序就相当于一个模块,接下来为该模块编写单元测试代码。
unittest 要求单元测试类必须继承 unittest.TestCase,该类中的测试方法需要满足如下要求:
- 测试方法应该没有返回值。
- 测试方法不应该有任何参数。
- 测试方法应以test 开头。
下面是测试用例的代码:
- import unittest
- from fk_math import *
- class TestFkMath(unittest.TestCase):
- # 测试一元一次方程的求解
- def test_one_equation(self):
- # 断言该方程求解应该为-1.8
- self.assertEqual(one_equation(5 , 9) , -1.8)
- # 断言该方程求解应该为-2.5
- self.assertTrue(one_equation(4 , 10) == -2.5 , .00001)
- # 断言该方程求解应该为27/4
- self.assertTrue(one_equation(4 , -27) == 27 / 4)
- # 断言当a == 0时的情况,断言引发ValueError
- with self.assertRaises(ValueError):
- one_equation(0 , 9)
- # 测试一元二次方程的求解
- def test_two_equation(self):
- r1, r2 = two_equation(1 , -3 , 2)
- self.assertCountEqual((r1, r2), (1.0, 2.0), '求解出错')
- r1, r2 = two_equation(2 , -7 , 6)
- self.assertCountEqual((r1, r2), (1.5, 2.0), '求解出错')
- # 断言只有一个解的情形
- r = two_equation(1 , -4 , 4)
- self.assertEqual(r, 2.0, '求解出错')
- # 断言当a == 0时的情况,断言引发ValueError
- with self.assertRaises(ValueError):
- two_equation(0, 9, 3)
- # 断言引发ValueError
- with self.assertRaises(ValueError):
- two_equation(4, 2, 3)
上面测试用例中使用断言方法判断函数的实际输出结果与期望输出结果是否一致,如果一致则表明测试通过,否则表明测试失败。
在上面的测试用例中,在测试 one_equation() 方法时传入了四组参数。至于此处到底需要传入几组参数进行测试,关键取决于测试者要求达到怎样的逻辑覆盖程度,随着测试要求的提高,此处可能需要传入更多的测试参数。当然,此处只是介绍 PyUnit 的用法示例,并未刻意去达到怎样的逻辑覆盖,这一点请务必留意。
在测试某个方法时,如果实际测试要求达到某种覆盖程度,那么在编写测试用例时必须传入多组参数来进行测试,使得测试用例能达到指定的逻辑覆盖。
unittest.TestCase 内置了大量 assertXxx 方法来执行断言,其中最常用的断言方法如表 1 所示。
断言方法 | 检查条件 |
---|---|
assertEqual(a, b) | a == b |
assertNotEqual(a, b) | a != b |
assertTrue(x) | bool(x) is True |
assertFalse(x) | bool(x) is False |
assertIs(a, b) | a is b |
assertIsNot(a, b) | a is not b |
assertIsNone(x) | x is None |
assertIsNotNone(x) | x is not None |
assertIn(a, b) | a in b |
assertNotIn(a, b) | a not in b |
assertlsInstance(a, b) | isinstance(a, b) |
assertNotIsInstance(a, b) | not isinstance(a, b) |
除了上面这些断言方法,如果程序要对异常、错误、警告和日志进行断言判断,TestCase 提供了如表 2 所示的断言方法。
断言方法 | 检查条件 |
---|---|
assertRaises(exc, fun, *args, **kwds) | fun(*args, **kwds) 引发 exc 异常 |
assertRaisesRegex(exc, r, fun, *args, **kwds) | fun(*args, **kwds) 引发 exc 异常,且异常信息匹配 r 正则表达式 |
assertWarns(warn, fun, *args, **kwds) | fun(*args, **kwds) 引发 warn 警告 |
assertWamsRegex(warn, r, fun, *args, **kwds) | fun(*args, **kwds) 引发 warn 警告,且警告信息匹配 r 正则表达式 |
assertLogs(logger, level) | With 语句块使用日志器生成 level 级别的日志 |
TestCase 还包含了如表 3 所示的断言方法用于完成某种特定检查。
断言方法 | 检查条件 |
---|---|
assertAlmostEqual(a, b) | round(a-b, 7) == 0 |
assertNotAlmostEqual(a, b) | round(a-b, 7) != 0 |
assertGreater(a, b) | a > b |
assertGreaterEqual(a, b) | a >= b |
assertLess(a, b) | a < b |
assertLessEqual(a, b) | a <= b |
assertRegex(s, r) | r.search(s) |
assertNotRegex(s, r) | not r.search(s) |
assertCountEqual(a, b) | a、b 两个序列包含的元素相同,不管元素出现的顺序如何 |
当测试用例使用 assertEqual() 判断两个对象是否相等时,如果被判断的类型是字符串、序列、列表、元组、集合、字典,则程序会自动改为使用如表 4 所示的断言方法进行判断。换而言之,如表 4 所示的断言方法其实没有必要使用,unittest 模块会自动应用它们。
断言方法 | 用于比较的类型 |
---|---|
assertMultiLineEqual(a, b) | 字符串(string) |
assertSequenceEqual(a, b) | 序列(sequence) |
assertListEqual(a, b) | 列表(list) |
assertTupleEqual(a, b) | 元组(tuple) |
assertSetEqual(a, b) | 集合(set 或 frozenset) |
assertDictEqual(a, b) | 字典(dict) |
运行测试
在编写完测试用例之后,可以使用如下两种方式来运行它们:
- 通过代码调用测试用例。程序可以通过调用 unittest.main() 来运行当前源文件中的所有测试用例。例如,在上面的测试用例中增加如下代码:
if __name__ == '__main__':
unittest.main() - 使用 unittest 模块运行测试用例。使用该模块的语法格式如下:
python -m unittest 测试文件
在使用 python -m unittest 命令运行测试用例时,如果没有指定测试用例,该命令将自动查找并运行当前目录下的所有测试用例。因此,程序也可直接使用如下命令来运行所有测试用例:
py -m unittest
采用上面任意一种方式来运行测试用例,均可以看到如下输出结果:
..
Ran 2 tests in 0.000s
OK
在上面输出结果的第一行可以看到两个点,这里的每个点都代表一个测试用例(每个以 test_ 开头的方法都是一个真正独立的测试用例)的结果。由于上面测试类中包含了两个测试用例,因此此处看到两个点,其中点代表测试用例通过。此处可能出现如下字符:
- .:代表测试通过。
- F:代表测试失败,F 代表 failure。
- E:代表测试出错,E 代表 error。
- s:代表跳过该测试,s 代表 skip。
在上面输出结果的横线下面看到了“Ran 2 tests in O.OOOs”提示信息,这行提示信息说明本次测试运行了多少个测试用例。如果看到下面提示 OK,则表明所有测试用例均通过。
上面的测试用例都可通过,是因为 fk_math.py 程序没有错误。如果将 fk_math.py 程序中的 ① 号代码故意修改为出错,假如将 ① 号代码修改为 return b/a,再次运行上面的测试用例,将会看到如下输出结果:
F.
======================================================================
FAIL: test_one_equation (__main__.TestFkMath)
----------------------------------------------------------------------
Traceback (most recent call last):
File "E:\test_fk_math.py", line 24, in test_one_equation
self.assertEqual(one_equation(5 , 9) , -1.8)
AssertionError: 1.8 != -1.8
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)
此时看到第一行的输出信息为 F,这表明第一个测试用例失败,第二个测试用例成功。
接下来在两条横线之间可以看到断言错误的 Traceback 信息,以及函数运行的实际输出结果和期望输出结果的差异。信息提示该函数运行的实际输出结果是 1.8,但期望输出结果是 -1.8。
Python unittest(PyUnit)单元测试框架的更多相关文章
- Python Unittest 自动化单元测试框架Demo
python 测试框架(本文只涉及 PyUnit) https://wiki.python.org/moin/PythonTestingToolsTaxonomy 环境准备 首先确定已经安装有Pyth ...
- python unittest+parameterized,单元测试框架+参数化
总要写新的自动化测试模块,在这里把demo记录下来,后面方便自己直接复制粘贴 from nose_parameterized import parameterized import unittest ...
- Python+selenium+unittest+HTMLTestReportCN单元测试框架分享
分享一个比较基础的,系统性的知识点.Python+selenium+unittest+HTMLTestReportCN单元测试框架分享 Unittest简介 unittest是Python语言的单元测 ...
- selenium pyunit单元测试框架
selenium pyunit单元测试框架 #PyUnit框架 #coding = utf - 8 #将要被测试的类 class Widget: def __int__(self,size = (40 ...
- python之使用单元测试框架unittest执行自动化测试
Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作. 单元测试框架即一堆工具的集合. 在说unittest ...
- python模块详解 | unittest(单元测试框架)(持续更新中)
目录: why unittest? unittest的四个重要概念 加载测试用例的三个方法 自动加载测试用例 忽略测试和预期失败 生成html测试报告 why unittest? 简介: Unitte ...
- Appium基于Python unittest自动化测试 & 自动化测试框架 -- PO并生成html测试报告
基于python单元测试框架unittest完成appium自动化测试,生成基于html可视化测试报告 代码示例: #利用unittest并生成测试报告 class Appium_test(unitt ...
- python之uinttest单元测试框架
unittest,是python中针对单元测试的一个测试框架 相当于python版的junit 简单举个例子: 如图,使用时,测试类需要继承单元测试TestCase这个类 必须要有setUp()和te ...
- python unittest自动测试框架
编写函数或者类时进行测试,确保代码正常工作 python unittest 模块提供了代码测试工具.按照定义测试包括两部分:管理测试依赖库的代码(称为‘固件’)和测试本身. 单元测试用于核实函数的某 ...
- Python+Selenium框架设计篇之-简单介绍unittest单元测试框架
前面文章已经简单介绍了一些关于自动化测试框架的介绍,知道了什么是自动化测试框架,主要有哪些特点,基本组成部分等.在继续介绍框架设计之前,我们先来学习一个工具,叫unittest. unit ...
随机推荐
- 数据分析 - Matplotlib
简介 Matplotlib是一个强大的Python绘图和数据可视化的工具包.数据可视化也是我们数据分析的最重要的工作之一,可以帮助我们完成很多操作,例如:找出异常值.必要的一些数据转换等.完成数据分析 ...
- 让iOS 开发更便捷-JSONConverter
JSONConverter是MAC上iOS开发的辅助小工具,可以快速的把json数据转换生成对应的模型类属性,目前支持Objective-C.Swift以及目前流行的第三方库: SwiftyJSON. ...
- Java逆变(Covariant)和协变(Contravariant)
1. 定义 逆变和协变描述的经过类型变换后的类型之间的关系.假如A和B表示类型,f表示类型变换,A ≤B表示A是B的子类型,那么 如果A ≤B,f(A) ≤f(B),那么f是协变 如果A ≤B,f(B ...
- 2019湖南省赛H题——概率转移&&逆矩阵
题意 题目链接 Bobo有一个 $n+m$ 个节点的有向图,编号分别为 $1 \sim n$,他还有一个 $n$ 行 $n+m$ 列的矩阵 $P$. 如果在 $t$ 时刻他位于节点 $u(1 \leq ...
- xunit.core 控制台输出日志
参考链接: https://www.cnblogs.com/dudu/p/9391959.html http://landcareweb.com/questions/15813/xunit-netbu ...
- (尚033)Vue_案例_slot(组件间的通信4:slot)
1.组件间的通信4:slot(slot:插槽,就是一个占位) slot用于标签反复使用很多次 1.1理解 此方式用于父组件向子组件传递标签数据, 其他为数据通信 外面组件向里面组件传递标签进去,直接拿 ...
- 假设检验总结以及如何用python进行假设检验(scipy)
几种常见的假设检验总结如下: 假设检验名称 Z检验 t检验 χ2检验 F检验 原假设 H0: μ≥μ0 H0: μ≤μ0 H0: μ=μ0 (比较样本和总体均值) ...
- .NET总结--WebService 配置与设置,发布
发环境 OS:win10 企业版 开发工具:VS2017 IIS版本:6.0 .NET版本:4.6.1 Web Service 简介 Web Service也叫XML Web Service WebS ...
- Python中的异步任务队列 arq
引言 最近在用 sanic 写东西,所有涉及到IO阻塞的代码都需要用 aio 的模块,好在近年来 asyncio 生态圈发展的还算不错,该有的都有 ~ 近期业务中 登录/注册 业务涉及的很复杂(涉及到 ...
- shell 编写进度条
test.sh #!/bin/bash i= bar='' label=("|" "/" "-" "\\") ] do ...