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

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

Facebook发布开源项目Tweaks,让iOS应用开发更加便捷 移动开发创业团队再下一城!UCloud与飞流战略合作 【走进支付宝】即将开场,问题大征集! 开发者薪资调查:半数开发者工作十年方可迎来好“薪”情 沟通更畅快 HipChat推出1对1视频和屏幕共享功能 AMD宣布支持DirectX 12 为游戏开发者带来更多福利 开发者薪资调查:2013年哪种编程语言最赚钱? 苹果挖墙脚未果,黑莓高管跳槽被起诉 不看不知道:六个超大规模Hadoop部署案例 豌豆荚发布应用内搜索技术协议 兼容App Indexing Oculus 21岁鬼才创始人:收购不等于改变 Unite China 2014课程解析:Unity教育行业专场免费开放 Instagram技术窥探,助你打造响应式App 切勿妄谈Hadoop,以及4个数据管道打造实践 一周消息树:雨果·巴拉与小米的那些事儿 《近匠》轻邮联合创始人Wesley:移动App要有撼动企业服务的野心 Facebook开源MySQL分支,谷歌、LinkedIn、Twitter等大拿捧场 淘宝明风:基于Graphx的图计算实践分享 调查显示:2015年Android将统治全球操作系统 初创公司最爱使用的那些开发工具 仙剑之父姚壮宪强势加盟Unity游戏及应用大赛 智能可视化平台:DCOS之数据中心抽象 一周热点:5美分/GB的开源存储系统,及硬盘质量“最差”的希捷 英特尔收购可穿戴技术公司BASIS Science 设计移动App的十大技巧 皮皮网陈超:易用与性能兼备,Spark蓬勃发展! Sahara顺利毕业,将加速OpenStack与Hadoop的融合 十大将拯救地球的开源硬件项目 Android应用程序开发者需要了解的五个内容 百度轻应用插件CloudaMySQL诞生记 盘点Github所用的开源项目 CB6的project Manager 让人头疼! 关于截取字符串的问题 请大家帮我看一下这程序 这样的菜单效果怎么做啊!! SendMessage()的问题... 一个被挂起的进程(失去热点)还能收到键盘的消息吗? cb6 的 project Manager 让人头疼! 求助,Apache设置问题 高手请进!!分,不够再给!!是关于session的问题!! samsung550s显示器一问,在线等待…… RedHat 8.0 启动进不了XWindow视窗,怎么办呢???? 在C#中如何完成byte[]之间的拷贝,或者部分拷贝 请问Fuction(T("helpnet"),int i)中的T是和作用? 在客户服务器系统中如何向服务器中请求查询数据? 连接SQL——PLUS问题,用connect / as sysdba,抱错:服务句柄未初始化 关于安装程序的问题,来者有分 急,请高手赐教,在线 初学C++,我是不是需要详细的了解模板技术 游戏开始时的图形菜单和控件是怎么做的? 请诸位将已解决,但长期不结账的case贴在这里。 前几天搞了一个小东西 抱着试试看 在csdn上发布了 没想到今天一看 已经有十多个朋友捧场进行了下载 ######请问,不创建窗体,怎么读出窗体中的控件的属性? 答对给500分! 这个痛苦的程序已经到最后关头,请大家一定要帮我一把:) 100分求优秀MySQL技术网站。 位图菜单... 如何让自己的程序不在Window任务管理器里面出现? 关于DTS数据库导入的问题 网页设计 怎么出现了这么怪的问题?……大家进来看一下阿,来者有分!! 问大家一个语法问题;下面的句子是什么含义? 怎么样从文件里读出数据? 大家注意一下,看看里面的内容,如果有自己的ID,请尽快结贴,要不将被强制结贴! 在asp.net内怎样表示局域网络上的共享目录的路径??? 如何让测量结构可通过WEB方式查看, 急急急, 请各位WEB开发高手帮帮忙 再当一年程序员,然后去卖血。心情沮丧,散分。 怎样把jpg文件设置为墙纸? 查询出字段中最大值的记录????字段为(number)型!在线等候! 请问新改版的CSDN是用ASP.Net开发的吗???我有事要请教 有没有对格斗游戏原理精通的朋友吗? javascript如何定制子框架(iframe)的onkeyup事件,在线等,谢啦! 有人知道该怎样解决吗??(在线) 急告:怎样向打开的批处理文件的命令提示符界面中输入参数 无聊兄,海良兄请进 基础问题:怎样能将一个int型变量转变为float型? 高分请教 VB中访问SQL 2000的表格语句? 各位大哥,给点意见 win2000 advance sever与win xp!xp不能上网浏览网页 有WEB开发经验的请进 请问谁有ocr的相关资料? 偶这样的人为什么找工作难? “回也,非助我者也”表现了孔子怎样的思想教育? To protect the Earth,we plant___trees every year.A.million of B.millions C.millionD.millions of2.It's impoetant for us to protect the Earth____we have only one Earth.A.because B.because of C.why D.so原因! Tom is said to have turned___ doctor in ___ early twenties.a:/;/b:/;hisc:a;thed:a;his 描写孔子的成语一个 七年级英语学习; turn it on .此句中“on"的词性是副词还是介词? tell me why 中文翻译 Poor Tom,he had his pocket lost yesterday.这个句子是错误的,为什么? 英语选择:-Turn off the light.-_________?A.Excuse me B.SorryC.Pardon D.Don‘t you选择并说明理由! Tom wanted to pay for his meal at the reataurant,but the waiter said,"No,you do not have to do thatToday is mo publican is birthday,so all the food you had is on the house ." on the house can you tell me why why why why why 一首英文歌中的歌词. does mr smith live next door to you——Does Mr Green live next door to you?——No,but he_______A,used to B,used to doC,used to be D,used to live求分析其他选项 英语翻译如 “ILOVE YOU”就是 “爱拉乌油” 沪江开心词场背单词效果怎么样?可以同时背多语种词书不? Do the Tom live next door to you A used to B used to do 为啥不选B表示过去啊 动词加er变名词 ready 如何加er 变名词ready 如果要变成名词,变成准备者之类的 如何加 Tom was ill the day before yesterday.(对划线部分提问)划线部分是illWhat was ______ ______ Tom the day before yesterday The mother didn't know who_____for the broken glass.A.blamed B.to blame C.be blamed D.would blame为什么要选B呢? 英语翻译In my dream'children sing A song of love for every boy and girl The sky is blue and fields are green And laughter is the language of the world Then i wake and all i see Is a world full of people in need Tell me why (why) does it have to b How was the weather ______in the mountain yesterday? The mother didn't know who ___ for the broken glass as it happened while she是 to blame,还是 to be blamed tell me why 歌词急 求孔子的个人简介500字左右,我要写一篇文章, I don't know______or not.A.whether he is at home B if he is at home C that he is at homeD whether is he at home The mother didn't know who to blame for the broken glass.为什么要用 to blame 为什么不是who was to blame ,而是who to blame ,不是有个短语是 sb be to blame 为什么今天沪江的开心词场打不开? another 、others、 the other、other、anyother的区别 Tom ——(meet)John at the airport yesterday.横线上是填meet还是meets 谁能详细比较at issue,in debate,in dispute的异同?要有例句,要有自己的解释,不是从别的地方复制 We know (that) the earth is not round We know the earth (that) is not round 哪句话是对的从句的位置?上面的that都不省略.第二个为什么错了?我想让“我们知道地球”这前半句做主语,我不想让“我们”做主 The mother didn't know who ____ for the broken glass.A.would blameB.was to blameC.blamedD.blames求答案及原因, debate on 和debate about 的区别是什么?RT anyother,the other,the others,another怎么区分? Sam was___ ___(被要求)meet Jim at the train station yesterday. quarral与debate的区别最好是用法上的区别.非常感谢! 为什么vivos7it手机不能使用沪江开心词场 The mother didn't know who to blame for the broken glass as it happened while she was out.为什么用who 而不用whom啊? dispute,debate,contention这仨词啥区别?如果表示一个社会问题的辩论 应该用哪个? The window is made ()glass. We all know that the earth is round.同义句转换:_____ _____ _____ to all,the earth is round. another other the other等一类词有什么区别? somebody broken the glass of the window.(反义疑问句) somebody broken the glass of the window_____ ________? Tell me why I can't be there where you are another和the other有什么区别?(当代词讲的时候)A:I’m sorry I have lost the pen that you gave meB:Don’t worry.There is () 用哪个? The window is broken.Try to ( ) who broke it.A.find outB.findC.lookD.look for 英文翻译、I can not tell me why you are pro Let's ______ something to eat.A.to have.B.having.C.have.D.has nightwish - I want my tears back歌词大意 He is going to have a glass of wine with (Tom).对括号里提问 let me to have a book改错 when did tom lose his new pen这句话对吗 ---Do you know ___ Lily is at home?---No,I donnot know.A.that B.whether let me have a book Where is the boy __that__ arm was hurt by the window _____ glass is broken?这里要填什么关系代词 关于number和amount的区别1、表示数量时,the number/amount of sth哪个接可数,哪个接不可数?2、它们前面是接large/big?3、它们本身可数不?若可数?怎么理解复数情况? have,let's,carrots,Mary,把他们连词成句 i want my tears 单选-Dad!Tom's broken a glass!-Dad!Tom's broken a glass!-_____.Accidents will happenA.No wayB.Doesn't matterC.No trouble at allD.Don't mention it 与孔子思想教育有关的成语 the window is broken是系表还是被动broken是用于系表 还是用于被动 怎么区分 Tom’s computer is broken.He’ll have it _______.
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘