PHPUnit的介绍可以翻翻前面几篇文章。

本文参考文献:

[Astels2006] A New Look at Test-Driven Development

在[Astels2006] A New Look at Test-Driven Development中,Dave Astels提出如下观点:
*极限编程(Extreme Programming)最初的原则是测试所有可能发生错误的地方。
*现在极限编程的测试实践已经进化到TDD(Test-Driven Development)
*但是工具依然强迫developer考虑测试用例和断言,而不是考虑需求说明。

Dave Astels原文:
So if it's not about testing, what's it about?
It's about figuring out what you are trying to do before you run off half-cocked to try to do it.
You write a specification that nails down a small aspect of behaviour in a concise, unambiguous, and executable form.
It's that simple. Does that mean you write tests? No. It means you write specifications of what your code will have to do.
It means you specify the behaviour of your code ahead of time. But not far ahead of time.
In fact, just before you write the code is best because that's when you have as much information at hand as you will up to that point.
Like well done TDD, you work in tiny increments... specifying one small aspect of behaviour at a time, then implementing it.

When you realize that it's all about specifying behaviour and not writing tests, your point of view shifts.
Suddenly the idea of having a Test class for each of your production classes is ridiculously limiting.
And the thought of testing each of your methods with its own test method (in a 1-1 relationship) will be laughable.

如果关键点不是测试,那是什么呢?
关键是在你马上就要做一件事之前指出你到底要做什么。你写了一份说明,详细到每一个细节的行为,简明、无歧义、可执行。
这如此简单。但那并不意味着你写了tests,你只是写了你的代码的功能说明书。这意味着你在写代码前详细分析了代码的行为。实际上,
等到你马上就要写代码时再去做这件事最好,因为这时你才能掌握关于这件事最详尽的信息。就像做的很好的TDD,你只需要多做一点点工作就OK。
一次分析清楚所有行为细小的方面,然后实现它。
当你意识到问题的关键是分析行为(specifying behaviour)而不是写tests,你的观念发生了变化。突然,每个类对应一个测试类的主意狭隘的可笑。

对你的每一个方法都用相对应的test method(自己的方法和测试脚本的方法数量一比一)去测试?这是个笑话。

 ---Dave Astels

行为驱动开发(BDD)的关注点是软件开发过程中的语言和交互。BDD的开发者用他们自己的语言和领域驱动设计(Domain-Driven Design)的普遍语言相结合,
来描述代码的行为和目的。这使得开发者关注代码的目的,而不是技术细节,最小化在代码编程语言和领域设计专家(domain experts)所说的领域语言之间的差异。

PHPUnit_Extensions_Story_TestCase类添加了一个story framework,便于“领域特定语言”(Domain-Specific Language,DSL)为行为驱动开发做定义。
在一个scenario里,given(),when(),then()各自都是一个步骤。and()类似于前置步骤。下面的方法在PHPUnit_Extensions_Story_TestCase中做了抽象声明,
需要实现他们:
    * runGiven(&$world, $action, $arguments)
      ...
    * runWhen(&$world, $action, $arguments)
      ...
    * runThen(&$world, $action, $arguments)
      ...

保龄球游戏例子
这里我们看看一个类,计算保龄球游戏的分数。规则如下:
    *游戏由10场组成
    *每一场player有2次机会来打倒10个瓶
    *每一场的分数=击倒的瓶子数 + strikes的bonus + spares的bonus
    *spare是指player在2次抛球中击倒全部10个瓶子。
     这一场的bonus就是下一次ROLL时击倒的瓶子数。
    *strike是指player第一次就击倒全部10个瓶子。
     这一场的bonus就是下两次ROLL时击倒的瓶子数。

下面看看这些规则怎么表述

代码

  1<?php
  2require_once 'PHPUnit/Extensions/Story/TestCase.php';
  3require_once 'BowlingGame.php';
  4 
  5class BowlingGameSpec extends PHPUnit_Extensions_Story_TestCase
  6{
  7    /**
  8     * @scenario
  9     */
 10    public function scoreForGutterGameIs0()
 11    {
 12        $this->given('New game')
 13             ->then('Score should be', 0);
 14    }
 15 
 16    /**
 17     * @scenario
 18     */
 19    public function scoreForAllOnesIs20()
 20    {
 21        $this->given('New game')
 22             ->when('Player rolls', 1)
 23             ->and('Player rolls', 1)
 24             ->and('Player rolls', 1)
 25             ->and('Player rolls', 1)
 26             ->and('Player rolls', 1)
 27             ->and('Player rolls', 1)
 28             ->and('Player rolls', 1)
 29             ->and('Player rolls', 1)
 30             ->and('Player rolls', 1)
 31             ->and('Player rolls', 1)
 32             ->and('Player rolls', 1)
 33             ->and('Player rolls', 1)
 34             ->and('Player rolls', 1)
 35             ->and('Player rolls', 1)
 36             ->and('Player rolls', 1)
 37             ->and('Player rolls', 1)
 38             ->and('Player rolls', 1)
 39             ->and('Player rolls', 1)
 40             ->and('Player rolls', 1)
 41             ->and('Player rolls', 1)
 42             ->then('Score should be', 20);
 43    }
 44 
 45    /**
 46     * @scenario
 47     */
 48    public function scoreForOneSpareAnd3Is16()
 49    {
 50        $this->given('New game')
 51             ->when('Player rolls', 5)
 52             ->and('Player rolls', 5)
 53             ->and('Player rolls', 3)
 54             ->then('Score should be', 16);
 55    }
 56 
 57    /**
 58     * @scenario
 59     */
 60    public function scoreForOneStrikeAnd3And4Is24()
 61    {
 62        $this->given('New game')
 63             ->when('Player rolls', 10)
 64             ->and('Player rolls', 3)
 65             ->and('Player rolls', 4)
 66             ->then('Score should be', 24);
 67    }
 68 
 69    /**
 70     * @scenario
 71     */
 72    public function scoreForPerfectGameIs300()
 73    {
 74        $this->given('New game')
 75             ->when('Player rolls', 10)
 76             ->and('Player rolls', 10)
 77             ->and('Player rolls', 10)
 78             ->and('Player rolls', 10)
 79             ->and('Player rolls', 10)
 80             ->and('Player rolls', 10)
 81             ->and('Player rolls', 10)
 82             ->and('Player rolls', 10)
 83             ->and('Player rolls', 10)
 84             ->and('Player rolls', 10)
 85             ->and('Player rolls', 10)
 86             ->and('Player rolls', 10)
 87             ->then('Score should be', 300);
 88    }
 89 
 90    public function runGiven(&$world, $action, $arguments)
 91    {
 92        switch($action) {
 93            case 'New game': {
 94                $world['game']  = new BowlingGame;
 95                $world['rolls'] = 0;
 96            }
 97            break;
 98 
 99            default: {
100                return $this->notImplemented($action);
101            }
102        }
103    }
104 
105    public function runWhen(&$world, $action, $arguments)
106    {
107        switch($action) {
108            case 'Player rolls': {
109                $world['game']->roll($arguments[0]);
110                $world['rolls']++;
111            }
112            break;
113 
114            default: {
115                return $this->notImplemented($action);
116            }
117        }
118    }
119 
120    public function runThen(&$world, $action, $arguments)
121    {
122        switch($action) {
123            case 'Score should be': {
124                for ($i = $world['rolls']; $i < 20; $i++) {
125                    $world['game']->roll(0);
126                }
127 
128                $this->assertEquals($arguments[0], $world['game']->score());
129            }
130            break;
131 
132            default: {
133                return $this->notImplemented($action);
134            }
135        }
136    }
137}
138?>

phpunit --story BowlingGameSpec
PHPUnit 3.4.2 by Sebastian Bergmann.

BowlingGameSpec
 [x] Score for gutter game is 0

Given New game
    Then Score should be 0

[x] Score for all ones is 20

Given New game
    When Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
     and Player rolls 1
    Then Score should be 20

[x] Score for one spare and 3 is 16

Given New game
    When Player rolls 5
     and Player rolls 5
     and Player rolls 3
    Then Score should be 16

[x] Score for one strike and 3 and 4 is 24

Given New game
    When Player rolls 10
     and Player rolls 3
     and Player rolls 4
    Then Score should be 24

[x] Score for perfect game is 300

Given New game
    When Player rolls 10
     and Player rolls 10
     and Player rolls 10
     and Player rolls 10
     and Player rolls 10
     and Player rolls 10
     and Player rolls 10
     and Player rolls 10
     and Player rolls 10
     and Player rolls 10
     and Player rolls 10
     and Player rolls 10
    Then Score should be 300

Scenarios: 5, Failed: 0, Skipped: 0, Incomplete: 0.

转载于:https://www.cnblogs.com/awaken/archive/2009/11/27/1612078.html

[PHP]用PHPUnit进行行为驱动开发(Behaviour-Driven Development)相关推荐

  1. Transformer课程 业务对话机器人Rasa 3.x 会话驱动开发(Conversation-Driven Development)

    Transformer课程 业务对话机器人Rasa 3.x 会话驱动开发(Conversation-Driven Development) Rasa 官网 https://rasa.com/ Conv ...

  2. 优美的测试代码 - 行为驱动开发(BDD)

    可理解的代码非常重要,测试代码也是如此.在我看来,优秀的测试代码,必须做到一个重要的事情就是保持测试逻辑的清晰.一个完整的测试案例通常包括三个部分: 1. SetUp 2. Exercise 3. V ...

  3. 敏捷开发系列之旅 第三站(认识FDD特征驱动开发)

    上篇文章中,我们探讨了什么是XP极限编程,以及极限编程的管理思想.核心价值观等等.在敏捷开发之旅的第三站,我想要和大家一起分享FDD特征驱动开发方法. 特征驱动开发--Feature Driven D ...

  4. 行为驱动开发BDD概要

    BDD脱胎于TDD 行为驱动开发(Behavior-Driven Development,简称BDD),是在测试驱动开发(Test-Driven Development,TDD)基础上发展而来的一种软 ...

  5. 行为驱动开发BDD和Cucunber简介

    测试驱动开发(TDD) 1.测试驱动开发,即Test-Driven Development(TDD),测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论.TDD的原理是在开发功能代码之前 ...

  6. 解读 TDD 测试驱动开发

    转自:http://www.jianshu.com/p/62f16cd4fef3 本文结构: 什么是 TDD 为什么要 TDD 怎么 TDD FAQ 学习路径 延伸阅读 什么是 TDD TDD 有广义 ...

  7. 简单的11步在Laravel中实现测试驱动开发

    测试驱动开发(英语:Test-driven development,缩写为TDD)是一种软件开发过程中的应用方法,由极限编程中倡导,以其倡导先写测试程序,然后编码实现其功能得名. 下文是我在Mediu ...

  8. 0、Spring 注解驱动开发

    0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...

  9. Android深度探索与驱动开发(一)

    Android移植可分为两个部分:应用移植和系统移植.应用移植是指将应用程序移植到某一特定硬件平台上.由于不同硬件平台之间的差异,Android SDK API也有可能存在差异,或者将应用程序从低版本 ...

最新文章

  1. 奇葩错误 -- modelsim波形显示no data(全X)
  2. Spring Cloud第一篇:服务注册与发现Eureka
  3. python外星人入侵游戏图片_跪求一个问题@关于外星人入侵游戏(《python编程基础从入门到实...
  4. JDK容器学习之ArrayList:底层存储和动态扩容
  5. 解决布局拖动混乱的问题
  6. 【腾许Bugly干货分享】“HTTPS”安全在哪里?
  7. 【Ansible】Ansible控制windows插件安装及运行error与解决方法
  8. OJ1180: 成绩统计(结构体专题)(C语言)
  9. java 滚动显示信息_滚动显示文本的Java程序
  10. 基本STRUTS标签-学习笔记-Bean标签
  11. c语言函数与宏定义的应用,C语言第3次实_函数与宏定义的应用.doc
  12. 用HTML5编写日历,js编写当天简单日历效果【实现代码】_javascript技巧
  13. Linux系统下VMware虚拟机安装Win7 ISO操作系统
  14. ROMS 编译upwelling.h
  15. 猫狗数据集百度网盘链接
  16. android修改文件名,android 修改文件名称
  17. k8s常见故障排查一点通
  18. Basemap绘制地图
  19. Zmeet云雾架构-融合AI能力的通讯层的变革,性能远超传统通讯技术
  20. 一个神器的插件:Piti

热门文章

  1. 专心做业务,别想不开搞研发
  2. Google MapReduce有啥巧妙优化?
  3. RESTful Web 服务 - 寻址
  4. 多线程:一些好的编程建议
  5. TCP核心概念-慢启动,ssthresh,拥塞避免,公平性的真实含义
  6. 【阿里云 Linux 服务器】购买 Linux 到项目部署过程中遇到的问题,部署 SpringBoot 项目到服务器上,在手机上安装 Android 程序进行测试
  7. Android --- 自定义ImageView 实现圆形图片
  8. python elseif用法_python学习笔记(一)
  9. 数据中心基础运维人员的职业规划
  10. 弱电机房保温棉知识汇总,它的种类及使用你懂多少?