您的位置:软件测试 > 开源软件测试 > 开源单元测试工具 >
追求代码质量: 亲身体验行为驱动开发
作者:网络转载 发布时间:[ 2013/2/25 14:28:41 ] 推荐标签:

杰出的 expectation 和 override

        在清单 3 中发生的一些事情是 JBhave 特有的,所以要解释一下。首先,我创建 Stack 类的一个实例,并将它限制为 String 类型(通过 Java 5 泛型)。接下来,我使用 JBehave 的 异常框架 实际建模我所期望的行为。 Ensure 类类似于 JUnit 或 TestNG 的 Assert 类型;但是,它增加了一系列方法,提供了更具可读性的 API(这常被称作文学编程)。在清单 3 中,我确保了如果对 null 调用 push(),则抛出一个 RuntimeException。

        JBehave 还引入了一个 Block 类型,它是通过用所需的行为覆盖 run() 方法来实现的。在内部,JBehave 确保期望的异常类型不被抛出(并因此被捕捉),而是生成一个故障状态。您可能还记得,在我前面关于 用 Google Web Toolkit 对 Ajax 进行单元测试 的文章中,也出现了类似的覆盖便利类的模式。在那种情况下,覆盖是通过 GWT 的 Timer 类实现的。

        如果现在运行清单 3 中的行为,应该看到出现错误。按照目前编写的代码,push() 方法不执行任何操作。所以不可能生成异常,从清单 4 中的输出可以看到这一点。


清单 4. 没有发生期望的行为
              
1) StackBehavior should throw exception upon null push:
VerificationException: Expected:
object not null
but got:
null:
 


        清单 4 中的句子 “StackBehavior should throw exception upon null push” 模拟行为的名称(shouldThrowExceptionUponNullPush()),并加上类的名称。 实际上,JBehave 是在报告当它运行所需的行为时,没有获得任何反应。当然,我的下一步是要使上述行为成功运行,为此我检查 null,如清单 5 所示。


清单 5. 在栈类中增加指定的行为
              
public void push(E value) {
  if(value == null){
   throw new RuntimeException("Can't push null");
  }
}
 


        当我重新运行行为时,一切都运行得很好,如清单 6 所示。


清单 6. 成功!
              
Time: 0.021s

Total: 1. Success!
 


行为驱动开发

        清单 6 中的输出与 JUnit 的输出是不是很像?这也许不是巧合,对不对?如前所述,JBehave 是根据 xUnit 范例建模的,它甚至通过 setUp() 和 tearDown() 提供了对 fixture 的支持。由于我可能在整个行为类中使用一个 Stack 实例,我可能也会将那种逻辑推入(这里并非有意使用双关语)到一个 fixture 中,正如清单 7 中那样。注意, JBehave 将与 JUnit 一样遵循相同的 fixture 规则 — 也是说,对于每个行为方法,它都运行一个 setUp() 和 tearDown()。


清单 7. JBehave 中的 fixture
              
public class StackBehavior {
 private Stack<String> stStack;
 
 public void setUp() {
  this.stStack = new Stack<String>();
 }
 //...
}
 


        对于接下来的行为方法,shouldThrowExceptionUponPopWithoutPush() 表示我必须确保它具有类似于 清单 3 中的 shouldThrowExceptionUponNullPush() 的行为。从清单 8 中可以看出,没有任何特别神奇的地方 — 有吗?


清单 8. 确保 pop 的行为
              
public void shouldThrowExceptionUponPopWithoutPush() throws Exception{
 
 Ensure.throwsException(RuntimeException.class, new Block() {
   public void run() throws Exception {
    stStack.pop();
   }
 });
}
 


        您可能已经清楚地知道,此时清单 8 并不会真正地编译,因为 pop() 还没有被编写。但是,在开始编写 pop() 之前,让我们考虑一些事情。

确保行为

        从技术上讲,在这里我可以将 pop() 实现为无论调用顺序如何,都只抛出一个异常。但是当我沿着这条行为路线前进时,我又忍不住考虑一个支持我所需要的规范的实现。在这种情况下,如果 push() 没有被调用(或者从逻辑上讲,栈为空)的情况下确保 pop() 抛出一个异常,则意味着栈有一个状态。正如之前 Linda 思考的那样,栈通常有一个 “内部容器”,用于实际持有项目。相应地,我可以为 Stack 类创建一个 ArrayList,用于保持传递给 push() 方法的值,如清单 9 所示。


        清单 9. 栈需要一种内部的方式来持有对象
              
public class Stack<E> {
 private ArrayList<E> list;

 public Stack() {
  this.list = new ArrayList<E>();
 }
 //...
}
 


        现在我可以为 pop() 方法编写行为,即确保当栈在逻辑上为空时,抛出一个异常。


清单 10. pop 的实现变得更容易
              
public E pop() {
 if(this.list.size() > 0){
  return null;
 }else{
  throw new RuntimeException("nothing to pop");
 }
}
 


        当我运行清单 8 中的行为时,一切如预期运行:由于栈中没有存在任何值(因此它的大小不大于 0),于是抛出一个异常。

        接下来的行为方法是 shouldPopPushedValue(),这个行为方法很容易指定。我只是 push() 一个值(“test”),并确保当调用 pop() 时,返回相同的值。

上一页1234下一页
软件测试工具 | 联系我们 | 投诉建议 | 诚聘英才 | 申请使用列表 | 网站地图
沪ICP备07036474 2003-2017 版权所有 上海泽众软件科技有限公司 Shanghai ZeZhong Software Co.,Ltd