您的位置:軟件測試 > 開源軟件測試 > 開源單元測試工具 >
追求代碼質量: 親身體驗行為驅動開發(fā)
作者:網絡轉載 發(fā)布時間:[ 2013/2/25 14:28:41 ] 推薦標簽:

清單 11. 如果將一個值入棧,那么出棧的也應該是它,對嗎?
              
public void shouldPopPushedValue() throws Exception{
 stStack.push("test");
 Ensure.that(stStack.pop(), m.is("test"));
}
 
為 Matcher 挑選 ‘M’

        在清單 11 中,我確保 pop() 返回值 “test”。在使用 JBehave 的 Ensure 類的過程中,您常常會發(fā)現(xiàn),需要一種更豐富的方式來表達期望。JBehave 提供了一種 Matcher 類型用于實現(xiàn)豐富的期望,從而滿足了這一需求。而我選擇重用 JBehave 的 UsingMatchers 類型(清單 11 中的 m 變量),所以可以使用 is()、and()、or() 等方法和很多其它整潔的機制來構建更具文學性的期望。

清單 11 中的 m 變量是 StackBehavior 類的一個靜態(tài)成員,如清單 12 所示。


清單 12. 行為類中的 UsingMatchers
              
private static final UsingMatchers m = new UsingMatchers(){};
 


        有了清單 11 中編寫的新的行為方法之后,現(xiàn)在可以來運行它 — 但是這時會產生一個錯誤,如清單 13 所示。


清單 13. 新編寫的行為不能運行
              
Failures: 1.

1) StackBehavior should pop pushed value:
java.lang.RuntimeException: nothing to pop
 


        怎么回事?原來是我的 push() 方法還沒有完工。回到 清單 5,我編寫了一個簡單的實現(xiàn),以使我的行為可以運行,F(xiàn)在是時候完成這項工作了,即真正將被推入的值添加到內部容器中(如果這個值不為 null)。如清單 14 所示。


清單 14. 完成 push 方法
              
public void push(E value) {
 if(value == null){
  throw new RuntimeException("Can't push null");
 }else{
  this.list.add(value);
 }
}
        但是,等一下 — 當我重新運行該行為時,它仍然失。


清單 15. JBehave 報告一個 null 值,而不是一個異常
              
1) StackBehavior should pop pushed value:
VerificationException: Expected:
same instance as <test>
but got:
null:
 

        至少清單 15 中的失敗有別于清單 13 中的失敗。在這種情況下,不是拋出一個異常,而是沒有發(fā)現(xiàn) "test" 值;實際彈出的是 null。仔細觀察 清單 10 會發(fā)現(xiàn):一開始我將 pop() 方法編寫為當內部容器中有項目時,返回 null。問題很容易修復。


清單 16. 是時候編寫完這個 pop 方法了
              
public E pop() {
 if(this.list.size() > 0){
  return this.list.remove(this.list.size());
 }else{
  throw new RuntimeException("nothing to pop");
 }
}
 


        但是,如果現(xiàn)在我重新運行該行為,我又收到一個新的錯誤。


清單 17. 另一個錯誤
              
1) StackBehavior should pop pushed value:
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
 


        仔細閱讀清單 17 中的實現(xiàn)可以發(fā)現(xiàn)問題:在處理 ArrayList 時,我需要考慮 0。


清單 18. 通過考慮 0 修復問題
              
public E pop() {
 if(this.list.size() > 0){
  return this.list.remove(this.list.size()-1);
 }else{
  throw new RuntimeException("Nothing to pop");
 }
}
 

棧的邏輯

        至此,通過允許傳遞多個行為方法,我已經實現(xiàn)了 push() 和 pop() 方法。但是我還沒有處理棧的實際內容,這是與多個 push() 和 pop() 相關聯(lián)的邏輯,間或出現(xiàn)一個 peek()。

        首先,我將通過 shouldPopSecondPushedValueFirst() 行為確保棧的基本算法(先進先出)無誤。


清單 19. 確保典型的棧邏輯
              
public void shouldPopSecondPushedValueFirst() throws Exception{
 stStack.push("test 1");
 stStack.push("test 2");
 Ensure.that(stStack.pop(), m.is("test 2"));
}
        清單 19 中的代碼可以按計劃運行,所以我將實現(xiàn)另一個行為方法(在清單 20 中),以確保兩次使用 pop() 都能表現(xiàn)出正確的行為。


清單 20. 更深入地查看棧行為
              
public void shouldPopValuesInReverseOrder() throws Exception{
 stStack.push("test 1");
 stStack.push("test 2");
 Ensure.that(stStack.pop(), m.is("test 2"));
 Ensure.that(stStack.pop(), m.is("test 1"));
}
        接下來,我要確保 peek() 能按預期運行。正如 Linda 所說,peek() 遵從和 pop() 相同的規(guī)則,但是 “應該保留棧頂的項目”。相應地,我在清單 21 中實現(xiàn)了 shouldLeaveValueOnStackAfterPeep() 方法的行為。


清單 21. 確保 peek 保留棧頂的項目
              
public void shouldLeaveValueOnStackAfterPeep() throws Exception{
 stStack.push("test 1");
 stStack.push("test 2");
 Ensure.that(stStack.peek(), m.is("test 2"));
 Ensure.that(stStack.pop(), m.is("test 2"));
}
        由于 peek() 還沒有定義,因此清單 21 還不能編譯。在清單 22 中,我定義了 peek() 的一個簡單的實現(xiàn)。


清單 22. 當前,peek 是必需的
              
public E peek() {
 return null;
}
        現(xiàn)在 StackBehavior 類可以編譯,但是它仍然不能運行。


清單 23. 返回 null 并不奇怪,對嗎?
              
1) StackBehavior should leave value on stack after peep:
VerificationException: Expected:
same instance as <test 2>
but got:
null:
        在邏輯上,peek() 不會從內部集合中移除 項目,它只是傳遞指向那個項目的指針。因此,我將對 ArrayList 使用 get() 方法,而不是 remove() 方法,如清單 24 所示。

清單 24. 不要移除它
              
public E peek() {
 return this.list.get(this.list.size()-1);
}
 

棧為空的情況

        現(xiàn)在重新運行 清單 21 中的行為,結果順利通過。但是,在這樣做的過程中發(fā)現(xiàn)一個問題:如果棧為空,則 peek() 有怎樣的行為?如果說棧為空時調用 pop() 會拋出一個異常,那么 peek() 是否也應該如此?

        Linda 對此沒有進行解釋,所以,顯然我需要自己添加新的行為。在清單 25 中,我為 “當之前沒有調用 push() 時調用 peek() 會怎樣” 這個場景編寫了代碼。

上一頁1234下一頁
軟件測試工具 | 聯(lián)系我們 | 投訴建議 | 誠聘英才 | 申請使用列表 | 網站地圖
滬ICP備07036474 2003-2017 版權所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd