说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

基于Python的测试驱动开发实战

HTML文档下载 WORD文档下载 PDF文档下载
顾名思义,TDD即进行编程时先把测试部分写好,当发现不能通过时,再进行编程以使测试通过。然后在这基础上适当地调整测试代码以实现更多功能,最后再编写代码使之实现。

【编者按】近年来测试驱动开发(TDD)受到越来越多的关注。这是一个持续改进的过程,能从一开始就形成规范,帮助提高代码质量。这是切实可行的而非天马行空的。

TDD的全过程是非常简单的。借助TDD,代码质量会得到提升,同时可以让你保持清晰的思路。TDD与敏捷开发可谓强强联合,特别是在进行结对编程的时候。本文主要介绍了TDD的核心概念,还有结合nosetest单元测试包进行Python示例简析。另外还会介绍一些Python备用包。

TDD是什么?

使用该方法可让你少走前人的弯路

顾名思义,TDD即进行编程时先把测试部分写好,当发现不能通过时,再进行编程以使测试通过。然后在这基础上适当地调整测试代码以实现更多功能,最后再编写代码使之实现。

TDD看起来非常像一个环,首先是要不断调整测试代码,然后是编码,改进,最后直至完成。先实现测试部分的做法会使你自然养成把问题放在首位的思维习惯。当真正去构建代码时,就不得不想清楚该如何把设计做好;比方说,该方法有何返回值?当遇到异常时该怎么办?诸如此类。

以这样的方式进行开发,意味着要想出不同的代码实现路径,并在测试中进行实践。这样做可使你少走前人的弯路:陷入一个问题后写出毫不相关的解决方案。


该过程可描述如下:

  • 写出一个缺陷单元测试
  • 使该单元测试通过
  • 重构

与敏捷开发结合

TDD与敏捷开发并行不悖甚至1+1远大于2,这里指的是代码质量而不是数量。

“这意味着结对双方都会参与其中,着重于当前工作,然后在每个环节进行互检。”

然而在结对编程时TDD是单独进行的。如果能把双方的开发流程混合好,互相都能理解就最好不过了。例如,其中一人写出单元测试,当测试通过后,另外一人可以编写不同的测试以之通过。

任何时候结对双方都可以互换角色,每半天或天。这意味着结对双方都会参与其中,每人都把精力放在当前任务上,然后在每个环节进行交叉互检。这难道不是一个双赢的做法吗?

TDD也可以是行为驱动开发过程中的组成部分,同样地,首先写出测试,只不过这里指的是接受测试。这样有助于把工作从头到尾都保持规范。

单元测试语法

进行单元测试时,使用到的Python方法如下:

  • assert: 编写个人声明的基本方式
  • assertEqual(a,b):检查a和b的是否等价
  • assertNotEqual(a,b):检查a和b的是否非等价
  • assertIn(a,b):检查是否存在b中
  • assertNotIn(a,b): 检查是否不存在b中
  • assertFalse(a):检查a的值是否为False
  • assertTrue(a):检查a的值是否为Ture
  • assertIsInstance(a,TYPE):检查a是否为“TYPE”类型
  • assertRaises(ERROR,a,args):以参数args调用a时,检查是否会出现ERROR

以上是实际当中使用频率最高的方法,更多的方法请查阅Python单元测试文档

安装并使用PythonNose

进行下面的练习前,请把nosetest测试运行包安装好。使用标准pip语句进行安装是最直接的做法。此外在项目中使用VirtualEnv(Python虚拟环境)也是不错的做法,因为它可确保所有包在不同项目中是独立的。假如对pip或VirtualEnv了解不多,不妨先查阅相关文档:VirtualEnv,PIP

pip语句十分简洁:

"pip install nose"
安装完成后,可以执行单个测试文件

$ nosetests example_unit_test.py

或者可以直接执行文件夹中的文件组

$ nosetests /path/to/tests
这里要注意的是每个测试方法都应以“test_”为开头,这样nosetest运行机才能正确识别出目标测试文件。

可选参数

下面介绍几个有用的命令行参数:

  • -v:输出更多信息,包括正在执行的测试文件名;
  • -s或-nocapture:进行PRINT语句输出,一般情况下这是隐藏的。开启后可方便调试;
  • --nologcapture:输出日志信息;
  • --rednose:一个可选插件,请点击这里下载,输出带颜色的输出信息;
  • --tags=TAGS:指定要执行的测试文件,而不是整个测试文件组。

实例分析和测试驱动方法

接下来结合一个简单的计算器类例子例如相加/相减,来讲述Python单元测试和TDD概念。对于add相加功能,会尝试编写一个缺陷测试。

在一个空白项目中,首先创建两个python包app和test。然后在每个文件里建立两个名为_init_.py空白文件。这是Phthon工程的标准结构,完成后可以拥有一个可导入的文件结构。如果需要了解更多有关文档架构的信息,请查阅Python包说明文档。 在测试目录里创建一个test_calulator.py文件,其代码如下:

import unittestclass TddInPythonExample(unittest.TestCase):    def test_calculator_add_method_returns_correct_result(self):        calc = Calculator()        result = calc.add(2,2)        self.assertEqual(4, result)

说明:

  • 首先,从Python标准库里导入标准的unittest模块
  • 接着,创建一个含有不同测试用例的类
  • 最后,创建以“test_”为开头的一个测试方法

完成后可着手编写测试代码了。执行方法前要先对计算器进行初始化,初始化完成后便可调用add方法,并把结果存入变量result中。完成后,使用unittest的assertEqual方法来确保add方法正常执行。

现在可以启动nosetest来执行测试文件了。代码如下:

if __name__ == '__main__':    unittest.main()

标准的Python文件执行方式为$ python test_calculator.py,相比之下本文使用的nosetests方法功能更丰富,例如可以运行目录中的全部测试文件。

$ nosetests test_calculator.pyE======================================================================ERROR: test_calculator_add_method_returns_correct_result (test.test_calculator.TddInPythonExample)----------------------------------------------------------------------Traceback (most recent call last):  File "/Users/user/PycharmProjects/tdd_in_python/test/test_calculator.py", line 6, in test_calculator_add_method_returns_correct_result    calc = Calculator()NameError: global name 'Calculator' is not defined ----------------------------------------------------------------------Ran 1 test in 0.001s FAILED (errors=1)
运行后可见出错的原因是没有导入Caculator。因为还没有创建呢!创建的方法是在app目录下建立calculator.py文件,然后导入:

class Calculator(object):    def add(self, x, y):        pass
import unittestfrom app.calculator import Calculator class TddInPythonExample(unittest.TestCase):    def test_calculator_add_method_returns_correct_result(self):        calc = Calculator()        result = calc.add(2,2)        self.assertEqual(4, result) if __name__ == '__main__':    unittest.main()

Caculator构建好之后,再次运行看会出现什么结果:

$ nosetests test_calculator.pyF======================================================================FAIL: test_calculator_add_method_returns_correct_result (test.test_calculator.TddInPythonExample)----------------------------------------------------------------------Traceback (most recent call last):  File "/Users/user/PycharmProjects/tdd_in_python/test/test_calculator.py", line 9, in test_calculator_add_method_returns_correct_result    self.assertEqual(4, result)AssertionError: 4 != None ----------------------------------------------------------------------Ran 1 test in 0.001s FAILED (failures=1)

很明显,add方法返回了错误的值,因为还没有为它指定行为。幸好nosetest会指出出错的位置,方便进行修改。稍作改动后,测试便可通过了:

class Calculator(object):    def add(self, x, y):        return x+y
$ nosetests test_calculator.py.----------------------------------------------------------------------Ran 1 test in 0.000s OK

虽然通过了,但是围绕该方法还可以做更多的工作。

沉迷于某个案例很容易造成短视

如果进行非数字型数据相加会导致什么后果呢?事实上Python是允许字符串或其它类型进行相加的,但在我们的例子里不允许。接着尝试就这个例子加入另一个缺陷测试,然后使用assertRaises方法来判断是否有异常抛出:

import unittestfrom app.calculator import Calculatorclass TddInPythonExample(unittest.TestCase):    def setUp(self):        self.calc = Calculator()    def test_calculator_add_method_returns_correct_result(self):        result = self.calc.add(2, 2)        self.assertEqual(4, result)    def test_calculator_returns_error_message_if_both_args_not_numbers(self):        self.assertRaises(ValueError, self.calc.add, 'two', 'three')if __name__ == '__main__':    unittest.main()

以上代码中,检查了是否引起了ValueError错误,其实还可以进行更多的检测,不过在这里不作深入讲述。此外,setup()方法用于推入计算对象。下面再看看nosetest会反馈什么信息:

$ nosetests test_calculator.py.F======================================================================FAIL: test_calculator_returns_error_message_if_both_args_not_numbers (test.test_calculator.TddInPythonExample)----------------------------------------------------------------------Traceback (most recent call last):  File "/Users/user/PycharmProjects/tdd_in_python/test/test_calculator.py", line 15, in test_calculator_returns_error_message_if_both_args_not_numbers    self.assertRaises(ValueError, self.calc.add, 'two', 'three')AssertionError: ValueError not raised ----------------------------------------------------------------------Ran 2 tests in 0.001s FAILED (failures=1)

显然nosetests告诉我们ValueError没有被抛出。现在我们有了一个新的缺陷测试,接着尝试编码进行解决:

class Calculator(object):    def add(self, x, y):        number_types = (int, long, float, complex)         if isinstance(x, number_types) and isinstance(y, number_types):            return x + y        else:            raise ValueError

代码中使用了isinstance方法是为了确保输入的是数字型数据。

由于两个变量的类型有多种组合,为了进行完整的测试,所以需要把可能出现的组合进行统筹并进行处理:

import unittestfrom app.calculator import Calculatorclass TddInPythonExample(unittest.TestCase):    def setUp(self):        self.calc = Calculator()     def test_calculator_add_method_returns_correct_result(self):        result = self.calc.add(2, 2)        self.assertEqual(4, result)     def test_calculator_returns_error_message_if_both_args_not_numbers(self):        self.assertRaises(ValueError, self.calc.add, 'two', 'three')     def test_calculator_returns_error_message_if_x_arg_not_number(self):        self.assertRaises(ValueError, self.calc.add, 'two', 3)     def test_calculator_returns_error_message_if_y_arg_not_number(self):        self.assertRaises(ValueError, self.calc.add, 2, 'three') if __name__ == '__main__':    unittest.main()

至此我们可以运行所有的测试了,所要实现的需求也都满足了。

其它的单元测试包

py.test

pytest的作用与nosetest类似,不过可以在单独的区域里输出信息,这意味着能够使我们很快地看清楚命令行中出现的打印信息。这对于只运行单个测试的情况是很有用的。

全国顶级域名根服务器21日下午疑遭黑客攻击 影响巨大 苹果下一代iPhone的十大预测 《近匠》第07期,专访《恶魔塔防》团队,看俄国复杂深奥的游戏文化 代码托管网站GitHub的总裁和CEO进行职位互换 使用Chromebook的五个指南 云中游终极声明:热酷侵权,有种正面回答少打太极 背后故事:英特尔Edison中国研发 内部评审中曾三次被拒 全栈工程师会是未来的发展趋势吗? 大数据整理:囊括分片、存储方法、扩展等多个方面 物联网 ,下一个云计算市场 一位数据挖掘工程师眼中的“大数据与企业的数据化运营” 揭开“iOS in the Car”的神秘面纱 与Apple对峙:法律途径,并非是我想要 Halfbrick新作市场表现低迷:前作辉煌难续 专访豌豆荚:融资,会给技术团队带来哪些影响? 前端开发必备 40款优秀CSS代码编写工具推荐 新环境下的新体验是互联网硬件火热的根本原因 信息安全救星 ——变形代码产品ShapeShifter AWS启动大规模降价,引领行业降价潮流 Windows 8.1 Update 1最新版的截图再遭曝光 2013年软件领域因缺陷导致的五大事件 专访AMD技术高管 详解Kaveri技术性能及新开发特性 网络的东西南北:从SDN到网络虚拟化 Bug让Chrome浏览器成了窃听器 联想23亿美元收购IBM X86服务器硬件及服务 Mozilla联手富士康推出Firefox OS平板 基于Web提供服务 GitHub中国游记最终回——开源与车库的碰撞 学以致用,光棍极客通过大数据搞定女朋友 1月24日:Mac步入了而立之年,生日快乐! 微软2014财年Q2财报:Surface营收翻倍 趣文:假如编程语言在一起聚餐 怪,文件路径绝对正确,EXE文件不能下载,其它格式正常。 在编写的用户控件中公开所引用控件的事件怎么实现啊 这是我的usb口的问题吗? 考程序员前想先考全国三级,请问三级种哪个科目考C编程?何时报名? 调用DLL时候出现Bad Dll calling convention错误,为什么?代码如下 如何建立一个小数据库? 我想问下,网络技术到底是指什么??? 为什么会出现这个问题呢??急救??? 在不同操作系统出现不能正常运行的问题,和默认字体语系乱码问题 C# 静态类成员 新手请求各位前辈帮助 如何查询我连接到哪个数据库? 哭着请求帮助。。。。。页面显示问题!!在线等着 网络技术到底是指什么???? 我想在<<三国九>>里输入正确的中文,有法子么? 能帮我改改么? 紧急求助,请高手支招 请问在JTABLE中如何将某几行设置成可以被选择的,其他几行设置成不能被选中的? 请问怎样修改SDI框架的标题及去掉最大化按钮??? 在自定义的用户控件上公开引用windows控件的事件怎么做啊!!谢谢 2003中为什么IDE设备只能用PIO模式? 在FAQ里见到这样一句,不明白什么意思 怎么我用vc 的 odbc 做数据库时,工程名为try ,在工程工作区的“class view”面板里没有tryview类的? 不能使用xmldocfile() 文档对象的问题 关于键盘纪录,高手 如何使用IPAddress控件? 初学者问:怎么用UpdateAllViews()? 无法创建asp.net应用程序 大家帮帮我吧,我都快崩溃了 中文乱码的问题 !!谁在VMWare 3.2里装过RedHat 9.0和RedFlag 3.0,请看看这些问题(100分)!! 如何使在输入框选中的文字在提交后仍然显示在输入框中? 无法创建asp.net应用程序 高手在上,请问怎么样在win2k中实现关闭程序和绘图[内详] 初学者问:如何在listview中查找其中一行? 怎样在调试窗口中看到变量值? 郁闷中,狂散分。。。。。。 DCOM怎么使用? 想做一个地区门户一般要包含那些栏目?? 线程 怎么能让DELPHI编出来的程序最小化以后进入到系统托盘? C++问题,请进来帮忙! help me ~!!! 以下行为究竟是关闭端口还是使用端口 callzjy(【剑客西门吹雪原来是仁者帮帮主...】) 怎么翻了这么多老帖子出来了??? 求原代码下载 大家都在干什么? 有什么东东大家可以一起研究研究? 初学者问:如何在listview中查找其中一行? fortran语言的高手请进,分数不够再加 我是一个刚刚步入JAVA世界的小鸟 恳请得到大家的帮助 。。 第三题.求道理.物理高二的. 请问“世界上,是鸡生蛋,还是蛋生鸡?” 谁能帮我看图写一段话 为什么自然光的反射和折射光都是偏振光大神们帮帮忙 电茶壶是将电能转化成什么能? 等腰梯形三角形AOB的面积是15cm2,线段OB的长度是OC的3倍,求梯形ABCD的面积是多少 甲乙两列火车长为92米和84米,若相向行驶,经过1.5秒完全错过(即从两车头相遇到两车尾离去)若同向行驶,则相遇后经过6秒超过,求两列火车的速度。 已知点P是圆F1:(x+1)2+y2=8上任意一点,点F2与点F1关于原点对称.线段PF2的中垂线m分(2011•惠州模拟)已知点P是圆F1:(x+1)2+y2=8上任意一点,点F2与点F1关于原点对称.线段PF2的中垂线m分别 帮我看图写一段话,急用啊, 甲、乙两列火车的长为92米和84米,若相向行驶,经过1.5秒完全错过(即从两车头相遇到两车尾离去),若同向行驶,则相遇后经过6秒超过,求两列火车的速度. 雕刻机切木板用什么刀最快18mm 厚的木板,不是实木.都是千层饼一样的木板.雕刻机用途主要是开板,不雕花.现在转速10000,30%进给速度.容易黑刀而且很慢,开一片板差不多两个小时.请问师傅开板 看图写一段话,100字 电源短路的路段电压是多少 我想知道这木板上的结是什么东西RT.需要很快的知道答案.我只要知道这的名字.不是虫眼.类似于结痂的一种东西形成的. 传动惯量是什么意思 一个质点在作匀速率圆周运动时(   ) A.加速度改变,B.加速度不变,C.一个质点在作匀速率圆周运动时(   )A.加速度改变,B.加速度不变,C.加速度改变,D.加速度不变,最好说明一下 为什么一块木板可以架得起比它重的东西? 平行四边形ABCD底边BC高是3cm,梯形ABCD与三角形ECD的面积是15cm2,长多少厘米? 如何测出一个电器是用直流电还是交流电? 矩形ABCD的对角线AC、BD相交于点O,过点C作BD的垂线与角BAD的平分线相较于点C,求证AC=CE 请帮忙看图写话 玻璃是拿什么做的 如图,矩形ABCD中,过点C作BD的垂线与角BAD的平分线相交于点E求证CE=BD 怎样使二氧化碳更多的溶于水中 长大后,我就成了你,才知道那块黑板写下的是真理……才知道那支粉笔画出的是彩虹.是哪首歌 一个平行四边形切割后可以拼成一个长方形,拼成的长方形长24厘米,宽5厘米.原来平行四边形的面积是( )平厘米?快 为什么二氧化碳溶于水中使水密度变大 通过波形如何判断音叉叉股的长短 关于灭蚊器我想买个灭蚊器,有没有人用过什么灭蚊器觉得效果比较好了随便说说觉得好就采取一楼 我想用非化学的二楼 我用过,效果很不理想三楼 我这里挂不了蚊帐 在三角形ABC中,点D是BC的中点,DE垂直AD,角EAD=角BAD.当角BAC=90,AB=8,AD=5时,求线段CE的长. 马拉着重为10000牛的车在水平公路上匀速前进,30min走了5.4km,车在前进的过程中受到的摩擦阻力是1000牛,(1)马拉车前进的速度是多大?(2)马对车的水平拉力是多大?马在这段时间内对车做了 英语翻译开展企业内部控制评审的实践探索内容摘要:如同“人无完人”一样,企业内部控制同样存在缺陷之处.如何弥补内控缺陷、完善内控执行系统,正是本文要探讨的内容.本文首先明确了 其中小球1固定在碗低A点,小球2可自由运动,平衡时小球2位于碗内的B位置处,改变小球2的电荷量把他放在图中 有人知道具体无线电测试仪产品认证的意思 甲火车长420米,每秒行15米.乙火车长390米,每秒行20米.乙追上甲并超过甲需要用几秒? 轻木板,(模型常用的那种)沈阳哪里有卖的 由两根绳子拉着小船匀速航行,若F1,F2的夹角为120,且F1=F2,则船所受的阻力F,F1,F2有怎样的关系 一辆车装满货物共重4.9乘10000牛,在平直公路上匀速行驶,在十分钟内前进了6千米.接上:在这时的功率为6.615乘10000瓦,求(1)这段时间内汽车的牵引力做了多少功(2)汽车所受重力做了多少功 矩形ABCD的对角线AC\BD相交于点O,过点C作BD的垂线与角BAD的平分线相交于点E,求证A求AC=CE 已知F1=10N,F1与F2的合力大小为10更号下3牛,合力与F1的夹角为30度,求F2的大小和方向 A,B两个小物块用轻绳连接,绳跨过倾角为30°的光滑斜面顶端的轻质滑轮,B悬空A放在斜面上A恰好静止第二次将B的质量改变发现A自斜面顶端由静止开始向下运动,经时间t速度大小为v已知物块A的 紫藤萝瀑布 如何理解"花和人都会遇到各种各样的不幸"? 一个圆形钟面的半径是15厘米,它的面积是多少平方米? 李伯伯参加飞镖比赛,投了4镖,成绩是37环.李伯伯至少有一镖为什么不低于10环,为什么?(例算式) 三棱镜顶角的测量报告怎么写? 一个分针长12厘米,走一昼夜的面积和周长?说出算式的意义 敲击音叉的力越大,则发出的声音()A越高B越低C越响D越轻 弧度,有没有数字来表示它? 一个圆形钟面的半径是15厘米,他的面积是多少平方米?清楚一点,要算式哦 已知圆F1:(X+1)^2+Y^2=1/4 F2:(X-1)^2+Y^2=49/4 动圆M与F1 F2都相切求1;动圆圆心M的轨迹方程 1.五边形ABCDE相似五边形A1B1C1D1E1,已知AB比A1B1=K,则它们的对角线长之比AC比A1C1= ,五边形ABCDE与五边形A1B1C1D1E1的周长之比为,面积之比为如图,在平行四边形ABCD中,EF//CD,分别交AB,CD于点E,F,AD=1,A 一个圆形钟面的直径是16厘米,它的周长是几厘米,它的面积是几平方厘米一个圆的半径扩大3倍,周长就扩大 倍,面积就扩大 倍丨两个圆的半径分别是3cm和5cm,它们的直径的比是 ,周长的比是 面积 北宋初期商人李某从海外经商归来准备到东京探亲.他从泉州上岸发现港口停泊着许多大船一片繁忙途径黄河域发现棉田遍布路过淮河流域闻到稻花飘香他选购了一套本朝史书当读到“恨金邦 电锯反转是什么原因 我国第一部磁悬浮列车已经在上海投入使用,如图所示是磁悬浮的原理,下面的圆柱是柱形磁铁,上面使用高温超导材料制成的超导圆环,将超导圆环放在柱形磁铁上,它就能在磁力的作用下悬浮 世界上到底是鸡生蛋还是蛋生鸡 如图,已知五边形ABCDE相似五边形A1B1C1D1E1,且AB=4cm,A1B1=3cm,连接AC,AD,A1C1,A1D1.试问五边形ABCDE和五边形A1B1C1D1E1的周长之比是多少 三角形ABC和三角形A1B1C1相似吗?三角形ABC与三角形A1B1D1面积之比是什 焦点在x轴上的椭圆上有一点P(3,4),F1、F2是椭圆的两个焦点?焦点在x轴上的椭圆上有一点P(3,4),F1、F2是椭圆的两个焦点,若PF1⊥PF2,求椭圆的标准方程? 世界上到底是鸡生蛋,还是蛋生鸡? 看图写一段话. 加拿大10岁男童发现超新星 为全球最分析称叙二次和议若破产叙利亚将成另一曝汪峰或哈林明年退出好声音穆尔西抵达开罗法庭 埃及高度戒备审判法国人卫仁博眼中的中国改革开放澳工业部长麦克法兰:中澳自由贸易协议蒙古国发现世界最大兽脚类恐龙筑巢地伊朗称希望与“六方”就核计划开展严肃百年一遇全环食壮观上演 美欧非观星族美国海军一高官被控受贿 其中包括性贿《好声音》第三季冠名2.5亿 浙江卫法国前部长称法国总统奥朗德头号问题是阿盟秘书长称支持卜拉希米就叙问题斡旋日本一司机醉酒驾车连撞5名女中学生后英国三百余名议员被指仍挪公款用于日常韩国总统朴槿惠巴黎会见联合国教科文组加拿大10岁男童发现超新星 为全球最中央印发通知要求开展“四风”突出问题存在环境违法 北京35家餐饮企业和单河南警方异地用警突查涉黄会所 搜出大南京富二代杀妻案开审 被告人称自首否20%年轻职工放弃养老保险 这样真的三亚凤凰机场查获濒危海洋物种“贝中之纳达尔称有信心完全康复征战里约 法网苹果或将在 WWDC 推出 13 或惊艳的Alpha 2,又一Magic儿童房4项装修基本原则,一定要记住!IP虽好仍需孵化平台助力 QQ浏览器这是一双带有水墨屏的智能运动鞋30年过去了,《三毛流浪记》中的三毛戴上VR头盔,到另一个世界学英语有了全民IP,就有了全民买买买,星球中港事件成行业热点 文交所规范或将开阿里影业为什么想用同人作家“取代”专泰国学校为防止作弊竟把答题卡做成了圆贴面热舞、同床共枕…这群鲜肉小CP的京车汇改装M5包围、M一轮毂 打造全张尧浠:日内市场评测及操作策略分析午后千万注意此点位!惊爆大洗盘!周二必将掀起大反攻!散户贷款精英交流会在京召开钱到位打造贷款地铁哺乳被批:公共场合不要裸露性器官
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘