PyUnit(unittest) 是 python的单元测试框架,可基于PyUnit编写和运行可重复执行的单元测试;
PyUnit 是 xUnit 体系的一个成员,xUnit 是众多测试框架的统统称(cppUnit、jUnit等),PyUnit 主要用于进行白盒测试和回归测试。
使用 PyUnit 具有如下好处:
PyUnit 具有如下三个主要特征:
更多详细的用法见:https://docs.python.org/3/library/unittest.html
所有测试的本质其实都是一样的,都是通过给定参数来执行函数,然后判断函数的实际输出结果和期望输出结果是否一致。
目前还有一种流行的开发方式叫作测试驱动开发,这种方式强调先编写测试用例,然后再编写函数和方法。假如程序要开发满足 A 功能的 fun_a() 函数,采用测试驱动开发的步骤如下:
测试驱动开发强调结果导向,也就是在开发某个功能之前,先定义好该功能的最终结果(测试用例关注函数的执行结果),然后再去开发该功能。就像建筑工人在砌墙之前,要先拉好一根笔直的绳子(作用相当于测试用例),然后再开始砌墙,这样砌出来的墙就会符合标准。所以说测试驱动开发确实是一种不错的开发方式。
下面开发一个简单的 funcation_demo.py 程序,该py文件包含几个常见的函数:加法、减法、乘法、除法、开方。
# -*- coding: utf-8 -*-
# @file : function_demo.py
# @author : shlian
# @date : 2019/8/22
# @version: 1.0
# @desc :
import math
def add(a,b):
return a+b
def sub(a,b):
return a-b
def multi(a,b):
return a*b
def div(a,b):
if b==0:
raise ValueError("invalid value:0")
return a/b
def sqrt(a):
if a<0:
raise ValueError("invalid value:{0}".format(a))
else:
return math.sqrt(a)
在定义好上面的 function_demo.py 程序之后,该程序就相当于一个模块,接下来为该模块编写单元测试代码。
unittest 要求单元测试类必须继承 unittest.TestCase,该类中的测试方法需要满足如下要求:
下面是测试用例的代码:
# -*- coding: utf-8 -*-
# @file : test_function_demo.py
# @author : shlian
# @date : 2019/8/22
# @version: 1.0
# @desc :
from function import function_demo
import unittest
import pysnooper
class test_function_demo(unittest.TestCase):
def setUp(self):
print("___________________________________init unit test____________________________________________________")
def tearDown(self):
print("___________________________________done______________________________________________________________")
@pysnooper.snoop()
def test_add_init(self):
self.assertEqual(first=3,second=function_demo.add(1,2))
@pysnooper.snoop()
def test_add_float(self):
self.assertEqual(first=3.789,second=function_demo.add(3,0.789))
@pysnooper.snoop()
def test_add_string(self):
self.assertEqual(first="abcdefg",second=function_demo.add("ab","cdefg"))
#********************************************************************************************************************
@pysnooper.snoop()
def test_sub_int(self):
self.assertTrue(5==function_demo.sub(105,100))
@pysnooper.snoop()
def test_sub_float(self):
self.assertNotEqual(first=5.78,second=function_demo.sub(10.79,5.01))#这里不等,思考一下为什么呢?
@pysnooper.snoop()
def test_sub_float2(self):
self.assertAlmostEqual(first=5.78,second=function_demo.sub(10.79,5.01),places=2)
#*********************************************************************************************************************
@pysnooper.snoop()
def test_multi_number(self):
self.assertEqual(first=24,second=function_demo.multi(3,8))
@pysnooper.snoop()
def test_multi_list(self):
self.assertListEqual(list1=['th','th','th','th','th','th'],list2=function_demo.multi(6,['th']))
@pysnooper.snoop()
def test_multi_str(self):
self.assertMultiLineEqual(first="aaaaaaaaaaaaaaaaa",second=function_demo.multi(17,"a"))
self.assertFalse("0123456789"==function_demo.multi(8,"0"))
#*********************************************************************************************************************
@pysnooper.snoop()
def test_div_int(self):
self.assertEqual(first=5,second=function_demo.div(100,20))
@pysnooper.snoop()
def test_div_float(self):
self.assertNotEqual(first=0.33333,second=function_demo.div(1,3))
@pysnooper.snoop()
def test_div_float2(self):
self.assertAlmostEqual(first=0.33333,second=function_demo.div(1,3),places=5)
@pysnooper.snoop()
def test_div_exception(self):
with self.assertRaises(Exception):
function_demo.div(1,0)
if __name__=="__main__":
unittest.main()
上面测试用例中使用断言方法判断函数的实际输出结果与期望输出结果是否一致,如果一致则表明测试通过,否则表明测试失败。
在测试某个方法时,如果实际测试要求达到某种覆盖程度,那么在编写测试用例时必须传入多组参数来进行测试,使得测试用例能达到指定的逻辑覆盖。
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) |
在编写完测试用例之后,可以使用如下两种方式来运行它们:
if __name__ == '__main__':
unittest.main()
python -m unittest 测试文件
在使用 python -m unittest 命令运行测试用例时,如果没有指定测试用例,该命令将自动查找并运行当前目录下的所有测试用例。因此,程序也可直接使用如下命令来运行所有测试用例:py -m unittest
采用上面任意一种方式来运行测试用例,均可以看到如下输出结果:
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_add_float>
09:57:23.011081 call 24 def test_add_float(self):
09:57:23.011081 line 25 self.assertEqual(first=3.789,second=function_demo.add(3,0.789))
09:57:23.011081 return 25 self.assertEqual(first=3.789,second=function_demo.add(3,0.789))
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_add_init>
09:57:23.013117 call 20 def test_add_init(self):
09:57:23.013117 line 21 self.assertEqual(first=3,second=function_demo.add(1,2))
09:57:23.013117 return 21 self.assertEqual(first=3,second=function_demo.add(1,2))
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_add_string>
09:57:23.014073 call 28 def test_add_string(self):
09:57:23.014073 line 29 self.assertEqual(first="abcdefg",second=function_demo.add("ab","cdefg"))
09:57:23.015099 return 29 self.assertEqual(first="abcdefg",second=function_demo.add("ab","cdefg"))
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_div_exception>
09:57:23.015099 call 69 def test_div_exception(self):
09:57:23.015099 line 70 with self.assertRaises(Exception):
09:57:23.015099 line 71 function_demo.div(1,0)
09:57:23.016067 exception 71 function_demo.div(1,0)
ValueError: invalid value:0
09:57:23.016067 return 71 function_demo.div(1,0)
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_div_float>
09:57:23.017065 call 61 def test_div_float(self):
09:57:23.017065 line 62 self.assertNotEqual(first=0.33333,second=function_demo.div(1,3))
09:57:23.017065 return 62 self.assertNotEqual(first=0.33333,second=function_demo.div(1,3))
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_div_float2>
09:57:23.018091 call 65 def test_div_float2(self):
09:57:23.018091 line 66 self.assertAlmostEqual(first=0.33333,second=function_demo.div(1,3),places=5)
09:57:23.018091 return 66 self.assertAlmostEqual(first=0.33333,second=function_demo.div(1,3),places=5)
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_div_int>
09:57:23.019060 call 57 def test_div_int(self):
09:57:23.020098 line 58 self.assertEqual(first=5,second=function_demo.div(100,20))
09:57:23.020098 return 58 self.assertEqual(first=5,second=function_demo.div(100,20))
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_multi_list>
09:57:23.021054 call 48 def test_multi_list(self):
09:57:23.021054 line 49 self.assertListEqual(list1=['th','th','th','th','th','th'],list2=function_demo.multi(6,['th']))
09:57:23.021054 return 49 self.assertListEqual(list1=['th','th','th','th','th','th'],list2=function_demo.multi(6,['th']))
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_multi_number>
09:57:23.022052 call 44 def test_multi_number(self):
09:57:23.022052 line 45 self.assertEqual(first=24,second=function_demo.multi(3,8))
09:57:23.022052 return 45 self.assertEqual(first=24,second=function_demo.multi(3,8))
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_multi_str>
09:57:23.024046 call 52 def test_multi_str(self):
09:57:23.024046 line 53 self.assertMultiLineEqual(first="aaaaaaaaaaaaaaaaa",second=function_demo.multi(17,"a"))
09:57:23.024046 line 54 self.assertFalse("0123456789"==function_demo.multi(8,"0"))
09:57:23.024046 return 54 self.assertFalse("0123456789"==function_demo.multi(8,"0"))
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_sub_float>
09:57:23.025043 call 36 def test_sub_float(self):
09:57:23.026040 line 37 self.assertNotEqual(first=5.78,second=function_demo.sub(10.79,5.01))#这里不等,思考一下为什么呢?
09:57:23.026040 return 37 self.assertNotEqual(first=5.78,second=function_demo.sub(10.79,5.01))#这里不等,思考一下为什么呢?
Return value:.. None
___________________________________done______________________________________________________________
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_sub_float2>
09:57:23.026040 call 40 def test_sub_float2(self):
09:57:23.026040 line 41 self.assertAlmostEqual(first=5.78,second=function_demo.sub(10.79,5.01),places=2)
09:57:23.027037 return 41 self.assertAlmostEqual(first=5.78,second=function_demo.sub(10.79,5.01),places=2)
Return value:.. None
___________________________________done______________________________________________________________
Ran 13 tests in 0.018s
OK
___________________________________init unit test____________________________________________________
Source path:... D:\shlian\github\python\project\unit_test\unit_tests\test_function_demo.py
Starting var:.. self = <test_function_demo.test_function_demo testMethod=test_sub_int>
09:57:23.028035 call 32 def test_sub_int(self):
09:57:23.028035 line 33 self.assertTrue(5==function_demo.sub(105,100))
09:57:23.028035 return 33 self.assertTrue(5==function_demo.sub(105,100))
Return value:.. None
___________________________________done______________________________________________________________
Process finished with exit code 0
这里的每个点都代表一个测试用例(每个以 test_ 开头的方法都是一个真正独立的测试用例)的结果。由于上面测试类中包含了两个测试用例,因此此处看到两个点,其中点代表测试用例通过。此处可能出现如下字符:
文章浏览阅读3k次,点赞6次,收藏5次。ubuntu18.04在运行sudo apt-get update命令时出现以下错误:E: 仓库 “http://ppa.launchpad.net/fcitx-team/nightly/ubuntu bionic Release” 没有 Release 文件解决办法:打开软件更新>其他软件,将做标记的两个勾选去掉问题解决...
文章浏览阅读1w次,点赞5次,收藏10次。1、 资金账户(证券公司开立的,与券商直接相关)资金账户是你登陆证券交易结算资金账户的凭证,你在一家证券公司开户后,就拥有了这家证券公司的资金账户,你平时用这个账户进行股票的买卖和操作。这是证券公司专门用来记录你资金流转的账户,但是你的资金并不在证券公司里,而是放在和证券公司合作的第三方存管银行账户里,你交易的时候通过交易软件把钱转到你的资金账户进行股票交易,这是为了保护投资者的利益,防止证券公司挪用和非法占有客户的资金。资金账号,是一种股市上的专业术语,一般指的是用于买卖股票的股东资金账户上的账..._证券账户与资金账户与银行账户区别
文章浏览阅读1.1k次。目录说明分解步骤输出示例其他类型的机器简版过程说明在运行 Cisco IOS 系统软件的 Catalyst 6500/6000 和 Cisco 7600 上,其启动顺序与 Cisco 7200 系列路由器有所不同,因为两者的硬件不一样。在您关机并重新开机机箱后,交换机处理器(SP)首先启动。在一小段时间(大约 25 到 60 秒)后,它将控制台所有权转交给路由处理器 (RP (MSFC))。RP 继续加载捆绑的软件映像。请务必在 SP 将控制台控制权转交给 RP 之后立即按 Ctrl-brk。如果您太早_sys-sp-3-logger_flushed system was paused for
文章浏览阅读427次。通过可视化工具建库建表创建数据库CREATE DATABASE studb2 CHAR SET utf8;切换数据库(使用use 将数据库切换到 studb2)USE studb2 ;在studb2 中创建名为t_stu的表CREATE TABLE t_stu( sid VARCHAR(10) , sname VARCHAR(20), age INT, height FLOAT , weight DOUBLE)CHAR SET utf8_头歌实践教学平台数据库用户数据库的创建及删除
文章浏览阅读120次。系统里的目标文件是按照特定的目标文件格式来组织的,各个系统的目标文件格式都不相同。从贝尔实验室诞生的第一个Unix系统使用的是a.out格式(直到今天,可执行文件仍然称为a.out文件)。Windows使用可移植可执行(PortableExecutable,PE)格式。Mac OS-X使用Mach-O格式。现代x86-64Linux和Unix系统使用可执行可链接格式(Executable and Linkable Format,ELF)。_windows readelf
文章浏览阅读3.6k次。用人工智能普惠体育发展。
文章浏览阅读1.5k次。单向图#include//每次找费用的最短路,更新残留网络图直到找不到最短路为止#include//最大费用 权值取负值 结果取负值#include#include#includeusing namespace std;const int inf=0x3f3f3f3f;struct Node_单向图费用流
文章浏览阅读318次。在现代编程世界中,面向对象编程(OOP)语言在改变软件开发中的设计和实现模式方面发挥了进化作用。作为OOP家族的重要成员,Python在过去10年左右逐渐流行起来。与其他OOP语言一样,Python围绕大量不同的对象操作其数据,包括模块、类和函数。如果您有任何OOP语言的编程经验,您应该知道所有对象都有其内部特征数据,称为字段、属性或属性。在Python中,这些对象绑定的特征数据通常称为属性。在本文中,我将特别在自定义类的上下文中讨论它们。1. 类属性为了更好地管理项目中的数据,我们经常需要_python属性的五大类
文章浏览阅读282次。5:系统简介设置:系统管理员应该可以通过系统简介设置功能设置系统前台的系统简介信息,系统前台的系统简介是随后台的变化而变化的,系统简介应该使用编辑器,实现图片,文字,列表,样式等多功能输入。6:系统公告设置:系统管理员应该可以通过系统公告设置功能设置系统前台的系统公告信息,系统前台的系统公告是随后台的变化而变化的,系统公告应该使用编辑器,实现图片,文字,列表,样式等多功能输入。应该都要能修改自己的登录密码,修改后需要重新登录。13:装修效果:员工给客户上传装修效果和装修进度,客户查询。_python抓取装修需求
文章浏览阅读2k次,点赞4次,收藏5次。ubuntu完美的nvidia驱动安装方式(ubuntu16+驱动410+cuda10.0) 本人卡 GeForce GTX TITAN X1.卸载驱动并重启电脑:sudo apt-get remove --purge nvidia-*sudo apt-get autoremove #特别重要sudo apt-get install -f #特别重要sudo reboot......_乌班图英伟达驱动选着哪个版本
文章浏览阅读5.3k次。报错内容:io.lettuce.core.RedisCommandTimeoutException: Connection initialization timed out. Command timed out after 1 minute(s) at io.lettuce.core.internal.ExceptionFactory.createTimeoutException(ExceptionFactory.java:65) ~[lettuce-core-6.1.4.RELEASE.j..._io.lettuce.core.rediscommandtimeoutexception: connection initialization time
文章浏览阅读454次。正交频分复用(OFDM)是一种在现代通信系统中广泛使用的调制技术,它具有高效的频谱利用和抗多径衰落等特点。64QAM(64-ary Quadrature Amplitude Modulation)是一种调制方式,可以在每个符号中传输更多的位信息。在OFDM系统中,保持载波同步对确保数据传输的可靠性至关重要。_基于ofdm+64qam系统的载波同步matlab仿真,