每一个可以努力的日子,都是一份厚礼。
测试驱动开发
测试 是软件开发中一个必不可少的环节。不管我们是否有意识到,其实我们经常都在做测试工作。比如最原始的在代码中插入 echo $variable
查看变量值,简单粗暴而有效。当然,PHP 也会有 XDebug 这样的工具,可以做到单步调试,不过要在服务器上安装再配合好本地一个 IDE 和浏览器一起运作,始终不是那么方便。
我们实现了一些复杂逻辑后,总会要输入一些数据,来确认程序是否能给出预期的结果,页面是否正常展示。更高级的开发者会写一些代码来自动完成这个测试过程,这样一来每当我们需要测试一些东西的时候,只需要调用测试脚本,就能直接获知结果了。这就是所谓的 自动测试。
软件测试已经发展为一个独立的工种,黑盒、白盒、单元测试、功能测试……流程都比较规范化了。随着软件开发过程的不断演变,各种方法论层出不穷,类似的名词包括:敏捷开发、持续集成、XP极限编程、结对编程、TDD……一般谈这些概念的时候都会被大牛黑出翔来,比如酷壳博主 @左耳朵耗子 发表过这样一条微博:
不会写程序的人来搞什么软件开发咨询,SQA,流程设计,软件项目管理,全是扯蛋。所以,程序员应该要像 Linus 一样自信的对这些人说:“Talk is cheap, show me the code.”
习总书记教育我们:
空谈误国。
我想聊一下 TDD。我必须声明我并非想借技术名词炒作概念。自从上次一篇 关于MVC的讨论 被新浪 SAE 的微博大号转发后,引来口水无数,各种被喷。其实我也只是初学者,谈谈自己对编程的理解,希望能尝试一下不同的实践方式而已,最终想法和所有开发者一样,都是要提高代码质量和生产效率。
TDD 简介
TDD 测试驱动开发,这个概念来自于敏捷中的极限编程(XP),其中的主要观点即:测试对项目成败起决定作用。测试应该自动化,经常测,并尽可能在功能代码还没实现之前就写好测试代码。在 XP 理论中,架构设计应该是恒定的不可随意改动的,而代码实现却会不断迭代和重构。
所谓的 测试驱动开发 的开发周期:
- 创建一个涵盖要实现的特性的新的测试。测试预计将在第一次执行的时候失败,因为特性尚未实现。
- 执行所有测试,确保这个新的测试是失败的。
- 编写代码来使得测试通过。
- 执行所有测试,确保所有测试通过。
- 重构新编写的代码并确保这些测试仍然能够通过。
重复步骤1至5推进整体功能的实现。
PHP 测试工具与实践
我所使用的 Yii Framework 支持单元测试和功能测试,这里会用到 PHPUnit 和 Selenium Remote Control 这两个测试工具。
PHPUnit 很多人都在用,通过很多断言语句(比如 assertTrue, assertEquals)来检查验证目标代码的行为。例如下面这个例子,用于测试博客评论功能。
// CDbTestCase 由 Yii 框架继承自 PHPUnit 中的 PHPUnit_Framework_TestCase class CommentTest extends CDbTestCase { public function testApprove() { // 插入一条新的评论 $comment=new Comment; $comment->setAttributes(array( 'content'=>'comment 1', 'status'=>Comment::STATUS_PENDING, 'createTime'=>time(), 'author'=>'me', 'email'=>'[email protected]', 'postId'=>$this->posts['sample1']['id'], ),false); $this->assertTrue($comment->save(false)); // 验证评论处于 pending 状态 $comment=Comment::model()->findByPk($comment->id); $this->assertTrue($comment instanceof Comment); $this->assertEquals(Comment::STATUS_PENDING,$comment->status); // 调用 approve() 方法后,再验证评论状态为“已通过” $comment->approve(); $this->assertEquals(Comment::STATUS_APPROVED,$comment->status); $comment=Comment::model()->findByPk($comment->id); $this->assertEquals(Comment::STATUS_APPROVED,$comment->status); } ...... } |
Selenium 这个工具的介绍看上去感觉很强大,可以通过模拟浏览器访问 Web 页面的方式,来进行 UI 测试。用法和上面类似,
// WebTestCase 由 Yii 框架继承自 PHPUnit_Extensions_SeleniumTestCase class PostTest extends WebTestCase { public function testShow() { $this->open('post/1'); // 验证测试页面文章标题 $this->assertTextPresent($this->posts['sample1']['title']); // 验证评论表单 $this->assertTextPresent('Leave a Comment'); } ...... } |
我的观点
TDD 并非教条。敏捷专家或是各种咨询师在宣扬敏捷,很多团队却被整得很苦逼,就是因为他们像宗教一样地信奉教条而不知变通。比如,TDD 就等同于 Unit Test 吗?我们真的一定要在写代码前就写好测试吗?这些问题都是值得商榷的,也是争论很多的。陈皓曾经谈到 TDD 的几个弊端——
- 1)TDD 可能会让程序员敷衍了事,以为 test case 没有错就正确了。
- 2)TDD 可能会让你忽略了软件设计和架构以及程序的扩展性和重用性。
令人沮丧的是,现在说这些有点纸上谈兵,因为我看到很多 PHP 程序员压根就没有写测试,自己手动查看页面没问题后就上线了,然后陷入无尽的 bug report 循环。这属于 各种流行的编程风格 中的撞大运编程。我觉得程序员编写单元测试还是很有必要的,虽然会增加工作量,但以后的代码维护会轻松很多。在 StackOverflow 上有人问 单元测试要测到什么程度,“老板为我的代码付报酬,而不是测试”。最值得借鉴的答案则强调并非面面俱到,只是特别注意边界情况,那些会让团队容易出错的代码。
众所周知,Facebook 没有专职的测试工程师,这是他们在质量控制中引以为豪并备受瞩目的一点。所有的测试都是程序员自己完成,对自己的代码质量负责,也就是 Eat your own dog food。每个程序员都是多面手,从最开始的一个 idea,到自己分析需求、架构,开发原型,自我管理版本控制,自己去测自己写的代码,自己去运维去部署。这样才会对整个开发的过程有深刻的认识,亲身体会某个环节的痛苦才会有想要改进的动力。
总结
每一个 PHP 程序员上辈子都是折翼的前端、美工、系统管理员和测试工程师……
这篇文章由lovelucy于2012-12-09 00:57发表在编程。你可以订阅RSS 2.0 也可以发表评论或引用到你的网站。除特殊说明外文章均为本人原创,并遵从署名-非商业性使用-相同方式共享创作协议,转载或使用请注明作者和来源,尊重知识分享。 |
批评不自由
则赞美无意义
Safari 600.1.4 iPhone iPhone OS 8_4_1 like Mac OS X) AppleWebKit 大约9年前
今天早上突然看不了,怎么办啊!