python 单元测试_Python 单元测试(unittest)
测试目录
项目的整体结构可以参考“软件目录开发规范”,这里单说测试目录。一般都是在项目里单独创建一个测试目录,目录名就是“tests”。关于目录的位置,一种建议是,在项目名(假设项目名是Foo)的一级子目录下创建二级子目录 “Foo/foo/tests” 。但是这样可能是因为用起来不方便,有很多是按下面的做法。不过下面的示例我还是用这个方法来创建测试目录。还可以把测试目录向上移一层,作为一级子目录,直接创建在项目之下 “Foo/tests”。参考django、scrapy、flask都是这样的做法。
测试函数
标题的意思是对函数(def)进行测试,相对于测试类(class)。学习测试,得有要测试的代码。下面是一个简单的函数,接收城市名和国家名,返回一个格式为“City, Country“这样的字符串:
# UnitTest/unit_test/utils/city_functions.py
def get_city_info(city, country):
city_info = "%s, %s" % (city, country)
return city_info.title()
接下来就对上面的这个函数进行测试。
手动测试
现在来写一个使用这个函数的程序:
# UnitTest/unit_test/test/cities.py
try:
from unit_test.utils.city_functions import get_city_info
except ModuleNotFoundError:
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from unit_test.utils.city_functions import get_city_info
print("Enter 'q' at any time to quit.")
while True:
city = input("city name: ")
if city == 'q':
break
country = input("country name: ")
if country == 'q':
break
fullname = get_city_info(city, country)
print("city info:", fullname)
然后运行的结果:
Enter 'q' at any time to quit.
city name: shanghai
country name: china
city info: Shanghai, China
city name: q
Process finished with exit code 0
上面这样是手动测试,还是得有一种自动测试函数输出的高效方式。如果能够对get_fullname()进行自动测试,就能始终确信,给这个函数提供测试过的姓名后,它能返回正确的结果。尤其是在对函数进行修改的前后。模块导入路径的问题PyCharm会自动把项目目录加到环境变量里去,在PyCharm里执行都没问题。但是如果不用PyCharm而是单独运行,这个目录结构应该会有点问题,会找不到需要测试的函数。简单点就是把测试用例和被测试的函数放到同一个目录里,然后改一下 from import 就可以正常运行了。或者自己手动添加环境变量,就像例子里那样。
单元测试-unittest
Python标准库中的模块unittest提供了代码测试工具。
创建测试用例为函数编写测试用例,可先导入模块unittest以及要测试的函数,再创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试。下面是一个只包含一个方法的测试用例:
# UnitTest/unit_test/test/test_city_functions.py
import unittest
try:
from unit_test.utils.city_functions import get_city_info
except ModuleNotFoundError:
import sys
sys.path.append('../..')
from unit_test.utils.city_functions import get_city_info
class CitiesTestCase(unittest.TestCase):
"""测试city_functions.py"""
def test_city_country(self):
city_info = get_city_info('shanghai', 'china')
self.assertEqual(city_info, 'Shanghai, China')
def test_New_York(self):
city_info = get_city_info('new york', 'America')
self.assertEqual(city_info, 'New York, America')
if __name__ == '__main__':
unittest.main()
命名的规则和建议:
类名,可以任意起名,但是最好看起来和测试有关并包含Test字样。
方法名,名字必须以“test_”开头,所有以“test_”开头的方法,都会自动运行
在测试的方法的最后,使用了unittest类最有用的功能之一:一个断言方法。来检查得到的结果和我们预期的结果是否一致。输出的效果最后一行 unittest.main() 让Python运行这个文件中的测试。执行程序后得到如下的输出:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
运行测试用例时,每完成一个单元测试,Python都打印一个字符:
测试通过时打印一个句点;
测试引发错误时打印一个E;
测试导致断言失败时打印一个F。
这就是你运行测试用例时,在输出的第一行中看到的句点和字符数量各不相同的原因。如果测试用例包含很多单元测试,需要运行很长时间,就可通过观察这些结果来获悉有多少个测试通过了。
PyCharm对单元测试做了自己的优化,输出看不到上面的点,而是有更加漂亮的展示方式。
测试不通过
现在看下测试不通过的效果。这里不修改测试用例,而是对get_city_info()函数做一个update,现在还要显示城市的人口数量:
def get_city_info(city, country, population):
city_info = "%s, %s - 人口: %d" % (city, country, population)
return city_info.title()
这次再执行测试用例,输出如下:
E
======================================================================
ERROR: test_city_country (__main__.CitiesTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_city_functions.py", line 17, in test_city_country
city_info = get_city_info('shanghai', 'china')
TypeError: get_city_info() missing 1 required positional argument: 'population'
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
这里看的是E而不是之前的点,表示有一个错误。
测试未通过的处理这里不要去修改之前的测试用例。假设update之前的函数已经在项目内使用起来了,现在测试不通过,表示之前调用这个函数的代码都有问题。如果不想改项目里其它的代码,这里先尝试修改get_city_info()函数,让它能够通过测试,也可以加上新的功能:
# UnitTest/unit_test/utils/city_functions.py
def get_city_info(city, country, population=None):
if population:
city_info = "%s, %s - 人口: %d" % (city, country, population)
else:
city_info = "%s, %s" % (city, country)
return city_info.title()
现在的各个版本的update才是兼容旧版本的代码,这次测试用例就可以通过了。
添加新测试之前的测试用例只能验证就的功能,现在添加了新功能,是否没问题,还得通过测试来进行验证:
# UnitTest/unit_test/test/test_city_functions.py
class CitiesTestCase(unittest.TestCase):
"""测试city_functions.py"""
def test_city_country(self):
city_info = get_city_info('shanghai', 'china')
self.assertEqual(city_info, 'Shanghai, China')
def test_New_York_population(self):
city_info = get_city_info('new york', 'America', 8537673)
self.assertEqual(city_info, 'New York, America - 人口: 8537673')
现在新功能的测试用例也用了,并且2个测试都能通过。以后如果还需要对get_city_info()函数进行修改,只要再运行测试就可以知道新的代码是否会对原有的项目有影响。
断言方法
模块在unittest.TestCase类中提供了很多断言方法,之前已经用一个了。下面是6个常用的断言方法:
assertEqual(a, b) : 核实a == b
assertNotEqual(a, b) : 核实a != b
assertTrue(x) : 核实x为True
assertFalse(x) : 核实x为False
assertIn(item, list) : 核实item在list中
assertNotIn(item, list) : 核实item不在list中
你只能在继承unittest.TestCase的类中使用这些方法。
测试类
前面的内容只是对函数进行测试。很多时候都会用到类,因为还需要能够证明类也可以正常的运行。类的测试与函数的测试相似,其中大部分工作都是测试类中方法的行为,但存在一些不同之处。
准备要测试的类
先编写一个类来进行测试,这个类里存储了一个课程名,以及学习该课程的学员:
# UnitTest/unit_test/course.py
class CourseManage(object):
def __init__(self, course):
self.course = course
self.students = []
def show_course(self):
print("课程:", self.course)
def add_student(self, name):
self.students.append(name)
def show_students(self):
print("所有学员:")
for student in self.students:
print('-', student)
为证明CourseManage类工作正常,再编写一个使用它的程序:
from unit_test.course import CourseManage
course = CourseManage("Python")
course.show_course()
print("准备录入学员...")
print("Enter 'q' at any time to quit.
")
while True:
resp = input("Student's Name: ")
if resp == 'q':
break
if resp:
course.add_student(resp.title())
print("
录入完毕...")
course.show_students()
这段程序定义了一门课程,并使用课程名创建了一个CourseManage对象。接下来主要就是调用对象的add_student()方法来录入学员名字。输入完毕后,按q能退出。最后会打印所有的学员。所有的输入和输出如下:
课程: Python
准备录入学员...
Enter 'q' at any time to quit.
Student's Name: oliver queen
Student's Name: barry allen
Student's Name: kara
Student's Name: sara lance
Student's Name: q
录入完毕...
所有学员:
- Oliver Queen
- Barry Allen
- Kara
- Sara Lance
Process finished with exit code 0
编写类的测试用例
下面来编写一个测试,对CourseManage类的行为的一个方面进行验证。如果用户输入了某个学员的名字,这个名字可以被存储在self.students的列表里。所以,需要做的是在学员被录入后,使用assertIn()这个断言方法:
# UnitTest/unit_test/test/test_course.py
import unittest
from unit_test.course import CourseManage
class TestCourseManage(unittest.TestCase):
def test_add_student(self):
course = CourseManage("Python")
name = 'snart'
course.add_student(name.title())
self.assertIn('Snart', course.students)
if __name__ == '__main__':
unittest.main()
上面的方法只验证了录入一个学员的情况,而大多数情况下都是有很多学员的。所以,还要添加一个方法,验证录入多个学员是否正常:
class TestCourseManage(unittest.TestCase):
def test_add_student(self):
course = CourseManage("Python")
name = 'snart'
course.add_student(name.title())
self.assertIn('Snart', course.students)
def test_add_students(self):
course = CourseManage("Python")
name_list = ['oliver queen', 'barry allen', 'kara', 'sara lance']
for name in name_list:
course.add_student(name.title())
for name in name_list:
self.assertIn(name.title(), course.students)
setUp() 方法
在上面的例子里,每个测试方法中都创建了一个实例。但是还有一种需求是,我希望只创建一个实例,但是要在多个方法里对这个实例进行操作来反复验证。在unittest.TestCase类包含方法setUp(),就可以只实例化一次,并可以在每个测试方法中使用。如果在TestCase类中包含了方法setUp(),Python会先运行它,再运行各个以test_打头的方法。简单点说,setUp()方法就是在父类里预留的一个钩子,会在其他测试方法运行前先运行:
import unittest
from unit_test.course import CourseManage
class TestCourseManage(unittest.TestCase):
def setUp(self):
self.course = CourseManage("Python")
self.name_list = ['oliver queen', 'barry allen', 'kara', 'sara lance']
def test_add_student(self):
name = 'snart'
self.course.add_student(name.title())
self.assertIn('Snart', self.course.students)
def test_add_students(self):
for name in self.name_list:
self.course.add_student(name.title())
for name in self.name_list:
self.assertIn(name.title(), self.course.students)
if __name__ == '__main__':
unittest.main()
测试自己编写的类时,使用setUp()方法会让测试方法编写起来更容易,下面是建议的做法:在setUp()方法中创建一系列实例并设置它们的属性,再在测试方法中直接使用这些实例。相比于在每个测试方法中都创建实例并设置其属性,这要容易得多。
小结
如果你在项目中包含了初步测试,其他程序员将更敬佩你,他们将能够更得心应手地尝试使用你编写的代码,也更愿意与你合作开发项目。如果你要跟其他程序员开发的项目共享代码,就必须证明你编写的代码通过了既有测试,通常还需要为你添加的新行为编写测试。请通过多开展测试来熟悉代码测试过程。对于自己编写的函数和类,请编写针对其重要行为的测试,但在项目早期,不要试图去编写全覆盖的测试用例,除非有充分的理由这样做。
pytest
这篇讲的是Python内置的单元测试模块。作为初学者先用着熟悉起来就很不错了。pytest是Python最流程的单测框架之一。具体可以上GitHub参考下那些开源项目的单元测试,很多用的是这个。
文章来源:https://blog.51cto.com/steed/2316436
python 单元测试_Python 单元测试(unittest)相关推荐
- python语言案例教程 单元测试_python单元测试unittest实例详解
本文实例讲述了python单元测试unittest用法.分享给大家供大家参考.具体分析如下: 单元测试作为任何语言的开发者都应该是必要的,因为时隔数月后再回来调试自己的复杂程序时,其实也是很崩溃的事情 ...
- [转载] python面面观单元测试_python 使用unittest进行单元测试
参考链接: 使用Unittest在Python中进行单元测试 importunittestimportHTMLTestRunner"""Python中有一个自带的单元测试 ...
- python为什么进行单元测试_python单元测试有什么好处
python单元测试有什么好处 发布时间:2020-11-09 11:33:13 来源:亿速云 阅读:81 作者:小新 这篇文章主要介绍python单元测试有什么好处,文中介绍的非常详细,具有一定的参 ...
- python第四章单元测试_Python 单元测试
编写单元测试用例,需要引入unittest 模块 编写的测试类需要从 unittest.TestCase继承 以test开头的方法就是测试方法,不以test开头的方法不是测试方法,运行时不被执行 对每 ...
- python单元测试_python单元测试
http://pyunit.sourceforge.net/ 目录 Python 单元测试框架 .................................................... ...
- python测试框架untest怎么循环执行_Python自动化测试-Unittest单元测试框架详解
python中unittest模块是用来做单元测试的. unittest是一个python版本的junit,junit是java中的单元测试框架,对java的单元测试,有一句话很贴切:Keep the ...
- python测试用例怎么写_Python单元测试unittest的具体使用示例
Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作. unittest是python的标准测试库,相比于其 ...
- python自动化测试框架有哪几种_Python自动化测试-Unittest单元测试框架详解
python中unittest模块是用来做单元测试的. unittest是一个python版本的junit,junit是java中的单元测试框架,对java的单元测试,有一句话很贴切:Keep the ...
- python单元测试教程_python单元测试unittest 输出测试报告HTMLTestRunner
建立unittest步骤: 1.先导入模块unittest及要测试的函数 2.再创建一个继承unittest.TestCase的类,类命名要包含Test 3.在类中创建一个方法,命名为test_开头 ...
最新文章
- python斑点检测
- python画散点图类型-绘制python中的线和散点图
- MySql数据库查询结果用表格输出PHP代码示例
- mysql is null走索引_Mysql数据库索引IS NUll ,IS NOT NUll ,!= 是否走索引
- CentOS 6.0正式版终于发布
- 点、圆和线的转化关系
- 深度学习之dropout
- MTC160-16-ASEMI可控硅模块MTC160-16
- R语言处理时间序列数据
- 分段函数的期望和方差_2014级《经济数学》课程教学大纲
- pythonqq机器人酷q_NoneBot+酷Q,打造QQ机器人
- db2的节点编目和数据库编目
- TRACE32——List源代码查看
- 神经网络能用来干什么_秒懂神经网络---震惊!!!神经网络原来可以这么简单!...
- 云有约 | 首攻RSA,天空卫士“秀肌肉”怎么样了?
- onload的具体使用
- php 跳转邮箱,实例详解JS简单实现点击跳转登陆邮箱功能的方法
- matlab对图像频谱图分析,应用Matlab对图像信号进行频谱分析及滤波
- pyqt制作简单的摄像头监控界面
- 纯干货,面试题分享,让你打有准备的战!
热门文章
- 2020-07-07 内模原理(The Internal Mode Principle)
- 事件 绑定,取消冒泡,拖拽 ,点击,事件委托习题
- c#中string的操作
- [C#] DataTable 操作汇总(持续更新)
- WPF 使用DMSkin for WPF 快速搭建漂亮的WPF程序
- Linux生成ssh公钥免密码登录远程主机和Xshell跨跳板机登录
- HOJ 2278 IP Filtering (二分)
- 聊一下我们团队的理念
- [VMM 2008虚拟化之初体验-2] 界面功能介绍
- BZOJ2212——线段树合并