python基础——单元测试
python基础——单元测试
如果你听说过“测试驱动开发”(TDD:Test-Driven Development),单元测试就不陌生。
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
  比如对函数abs(),我们可以编写出以下几个测试用例:
- 输入正数,比如 - 1、- 1.2、- 0.99,期待返回值与输入相同;
- 输入负数,比如 - -1、- -1.2、- -0.99,期待返回值与输入相反;
- 输入 - 0,期待返回- 0;
- 输入非数值类型,比如 - None、- []、- {},期待抛出- TypeError。
 
把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。
如果单元测试通过,说明我们测试的这个函数能够正常工作。如果单元测试不通过,要么函数有bug,要么测试条件输入不正确,总之,需要修复使单元测试能够通过。
  单元测试通过后有什么意义呢?如果我们对abs()函数代码做了修改,只需要再跑一遍单元测试,如果通过,说明我们的修改不会对abs()函数原有的行为造成影响,如果测试不通过,说明我们的修改与原有行为不一致,要么修改代码,要么修改测试。
这种以测试为驱动的开发模式最大的好处就是确保一个程序模块的行为符合我们设计的测试用例。在将来修改的时候,可以极大程度地保证该模块行为仍然是正确的。
  我们来编写一个Dict类,这个类的行为和dict一致,但是可以通过属性来访问,用起来就像下面这样:
>>> d = Dict(a=1, b=2)
>>> d['a']
1
>>> d.a
1
  mydict.py代码如下:
class Dict(dict):
    def __init__(self, **kw):
        super().__init__(**kw)
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
    def __setattr__(self, key, value):
        self[key] = value
  为了编写单元测试,我们需要引入Python自带的unittest模块,编写mydict_test.py如下:
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):
    def test_init(self):
        d = Dict(a=1, b='test')
        self.assertEqual(d.a, 1)
        self.assertEqual(d.b, 'test')
        self.assertTrue(isinstance(d, dict))
    def test_key(self):
        d = Dict()
        d['key'] = 'value'
        self.assertEqual(d.key, 'value')
    def test_attr(self):
        d = Dict()
        d.key = 'value'
        self.assertTrue('key' in d)
        self.assertEqual(d['key'], 'value')
    def test_keyerror(self):
        d = Dict()
        with self.assertRaises(KeyError):
            value = d['empty']
    def test_attrerror(self):
        d = Dict()
        with self.assertRaises(AttributeError):
            value = d.empty
  编写单元测试时,我们需要编写一个测试类,从unittest.TestCase继承。
  以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。
  对每一类测试都需要编写一个test_xxx()方法。由于unittest.TestCase提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。最常用的断言就是assertEqual():
self.assertEqual(abs(-1), 1) # 断言函数返回的结果与1相等
  另一种重要的断言就是期待抛出指定类型的Error,比如通过d['empty']访问不存在的key时,断言会抛出KeyError:
with self.assertRaises(KeyError):
value = d['empty']
  而通过d.empty访问不存在的key时,我们期待抛出AttributeError:
with self.assertRaises(AttributeError):
value = d.empty
运行单元测试
  一旦编写好单元测试,我们就可以运行单元测试。最简单的运行方式是在mydict_test.py的最后加上两行代码:
if __name__ == '__main__':
unittest.main()
  这样就可以把mydict_test.py当做正常的python脚本运行:
$ python mydict_test.py
  另一种方法是在命令行通过参数-m unittest直接运行单元测试:
  
这是推荐的做法,因为这样可以一次批量运行很多单元测试,并且,有很多工具可以自动来运行这些单元测试。
setUp与tearDown
  可以在单元测试中编写两个特殊的setUp()和tearDown()方法。这两个方法会分别在每调用一个测试方法的前后分别被执行。
  setUp()和tearDown()方法有什么用呢?设想你的测试需要启动一个数据库,这时,就可以在setUp()方法中连接数据库,在tearDown()方法中关闭数据库,这样,不必在每个测试方法中重复相同的代码:
class TestDict(unittest.TestCase):
    def setUp(self):
        print('setUp...')
    def tearDown(self):
        print('tearDown...')
  可以再次运行测试看看每个测试方法调用前后是否会打印出setUp...和tearDown...。
小结
单元测试可以有效地测试某个程序模块的行为,是未来重构代码的信心保证。
单元测试的测试用例要覆盖常用的输入组合、边界条件和异常。
单元测试代码要非常简单,如果测试代码太复杂,那么测试代码本身就可能有bug。
单元测试通过了并不意味着程序就没有bug了,但是不通过程序肯定有bug。
参考源码:
mydict.py:
#python 单元测试 示例
#该文件编写一个Dict类,这个类的行为和dict一致,但是可以通过属性来访问
#2016-8-31 21:57:18
#MengmengCoding
# -*- coding: utf-8 -*- class Dict(dict): def __init__(self,**kw):
super().__init__(**kw) #继承dict类的初始化功能 def __getattr__(self,key):
#当使用点号获取实例属性时,如果属性不存在就自动调用__getattr__方法
#调用时,执行self[key],此为dict类的方法,只要key存在,即可正确执行
#如此,即可使用‘.’运算符实现字典的查看功能
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key) def __setattr__(self,key,value):
#设置类实例属性时自动调用,如j.name=5 就会调用__setattr__方法
#该方法执行self[key]=value,此为dict类中字典的赋值方法。
#如此,即可实现使用‘.’运算符实现字典的赋值功能
self[key]=value
mydict_test.py:
#python 单元测试 示例
#该文件编写单元测试用例,用于测试mydict.py中的Dict类的方法
#2016-8-31 22:21:52
#MengmengCoding
# -*- coding: utf-8 -*- #为了编写单元测试,我们需要引入Python自带的unittest模块 import unittest from mydict import Dict class TestDict(unittest.TestCase): def setUp(self): #每调用一个测试方法前会执行
print('setUp...') def tearDown(self): #每调用一个测试方法后会执行
print('testDown...')
#以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。
def test_init(self): #测试Dict类是否能同dict一样初始化
d=Dict(a=1,b='test')
self.assertEqual(d.a,1)
self.assertEqual(d.b,'test')
self.assertTrue(isinstance(d,dict)) def test_key(self): #测试键值是否对应
d=Dict()
d['key']='value'
self.assertEqual(d.key,'value') def test_attr(self): #测试属性方法是否正确
d=Dict()
d.key='value' #测试‘.’属性和赋值属性
self.assertTrue('key' in d)#d中是否有key键
self.assertEqual(d['key'],'value')#测试d.key='value'这种赋值方法是否奏效 def test_keyerror(self):
d=Dict()
with self.assertRaises(KeyError): #期待抛出指定类型的Error(KeyError)
value=d['empty'] def test_attrerror(self):
d=Dict()
with self.assertRaises(AttributeError): #期待抛出指定类型的Error(AttributeError)
value=d.empty if __name__ =='__main__':
unittest.main()
python基础——单元测试的更多相关文章
- python基础===单元测试unittest
		''' 编写一个名为Employee 的类,其方法__init__()接受名.姓和年薪,并 将它们都存储在属性中.编写一个名为give_raise()的方法,它默认将年薪增加5000 美元,但也能够接 ... 
- Python基础+Pythonweb+Python扩展+Python选修四大专题 超强麦子学院Python35G视频教程
		[保持在百度网盘中的, 可以在观看,嘿嘿 内容有点多,要想下载, 回复后就可以查看下载地址,资源收集不易,请好好珍惜] 下载地址:http://www.fu83.cc/ 感觉文章好,可以小手一抖 -- ... 
- Python之路,Day4 - Python基础4 (new版)
		Python之路,Day4 - Python基础4 (new版) 本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件目录结构规范 作业:ATM项目开发 ... 
- Day4 - Python基础4 迭代器、装饰器、软件开发规范
		Python之路,Day4 - Python基础4 (new版) 本节内容 迭代器&生成器 装饰器 Json & pickle 数据序列化 软件目录结构规范 作业:ATM项目开发 ... 
- python - unittest 单元测试学习
		单元测试 单元测试是用来对一个模块.一个函数或者一个类进行正确性检验的测试工作 比如对Python中的abs 的测试 输入正数: 比如 1, 2, 3, 返回值不变 输入负数: 比如 -1, -2, ... 
- python基础系列教程——Python3.x标准模块库目录
		python基础系列教程——Python3.x标准模块库目录 文本 string:通用字符串操作 re:正则表达式操作 difflib:差异计算工具 textwrap:文本填充 unicodedata ... 
- python基础语法(一)
		Python的特点 1. 简单 Python是一种代表简单思想的语言. 2. 易学 Python有极其简单的语法. 3. 免费.开源 Python是FLOSS(自由/开放源码软件)之一. 4. 高层语 ... 
- Python基础语法(转)
		作者:Peter 出处:http://www.cnblogs.com/Peter-Zhang/ Python 基础语法(一) Python的特点 1. 简单 Python是一种代表简单思想的语言. ... 
- python基础知识的学习和理解
		参考链接:https://github.com/yanhualei/about_python/tree/master/python_learning/python_base python基础知识笔 ... 
随机推荐
- Android Studio升级后,开启时遇到tools.jar seems to be not in Android Studio错误?
			工作半年多,电子工程小白一枚.今天上班的时候,与几位同事聊到博客的问题.平时都是在别人的博客里找到问题的解决之妙法, 今天一个冲动之下,我决定也开始用博客记录工作的点滴,暂且当作笔记一用. 出于工作需 ... 
- 学C++50条建议
			1.把C++当成一门新的语言学习(和C没啥关系!真的.): 2.看<Thinking In C++>,不要看<C++变成死相>: 3.看<The C++ Programm ... 
- [BZOJ1056][BZOJ1862][HAOI2008][Zjoi2006]排名系统
			[BZOJ1056][BZOJ1862][HAOI2008][Zjoi2006]排名系统 试题描述 排名系统通常要应付三种请求:上传一条新的得分记录.查询某个玩家的当前排名以及返回某个区段内的排名记录 ... 
- 瀑布流js排列
			var _px = document.getElementById("px"); var con=document.getElementById("content&quo ... 
- OOP复习笔记
			/*OOP相关的代名词不做讲解*/ OOP的三大特征: 封装 - 继承 - 多态 -----------------------------------目录---------------------- ... 
- union联合体
			今天笔试的一道题,好久没用union了,竟然忘光光了. 关于其大小的计算,分两步:先算对齐大小(成员中字节最大的那个),再算分配空间: 不仅是对齐大小的整数倍,还要满足实际大小不能小于最大成员大小. ... 
- spring 部分配置内容备忘
			1.spring定时器简单配置: <bean name="taskJob" class="com.netcloud.mail.util.TaskJob"& ... 
- mysql 将指定列的浮点数转化为整数
			mysql 将指定列的浮点数转化为整数: update A set B = cast(B as decimal(10,0)) -- 或者 update A set B = round(B,0) 例 ... 
- ubuntu linux 下wine的使用
			ubuntu linux 下wine的使用 之前写了一篇 ubuntu15.10下编译安装wine1.8rc4 这一篇是来写它的使用的. 1.安装Wine支持包 现在,需要安装非开源(但免费)的支持包 ... 
- net-snmp的MIBs扩展(linux下)
			net-snmp的MIBs扩展 net-snmp的MIBs扩展 1 编译安装net-snmp 2 编写MIB文件 MIB文件描述 一个简单的示例 3 使自定义的MIB文件生效 4 实现agent代理程 ... 
