您的位置:軟件測(cè)試 > 開源軟件測(cè)試 > 開源單元測(cè)試工具 >
追求代碼質(zhì)量: 親身體驗(yàn)行為驅(qū)動(dòng)開發(fā)
作者:網(wǎng)絡(luò)轉(zhuǎn)載 發(fā)布時(shí)間:[ 2013/2/25 14:28:41 ] 推薦標(biāo)簽:

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

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

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


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


        有了清單 11 中編寫的新的行為方法之后,現(xiàn)在可以來(lái)運(yùn)行它 — 但是這時(shí)會(huì)產(chǎn)生一個(gè)錯(cuò)誤,如清單 13 所示。


清單 13. 新編寫的行為不能運(yùn)行
              
Failures: 1.

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


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


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


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

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


清單 16. 是時(shí)候編寫完這個(gè) 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)在我重新運(yùn)行該行為,我又收到一個(gè)新的錯(cuò)誤。


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


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


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

棧的邏輯

        至此,通過(guò)允許傳遞多個(gè)行為方法,我已經(jīng)實(shí)現(xiàn)了 push() 和 pop() 方法。但是我還沒(méi)有處理?xiàng)5膶?shí)際內(nèi)容,這是與多個(gè) push() 和 pop() 相關(guān)聯(lián)的邏輯,間或出現(xiàn)一個(gè) peek()。

        首先,我將通過(guò) shouldPopSecondPushedValueFirst() 行為確保棧的基本算法(先進(jìn)先出)無(wú)誤。


清單 19. 確保典型的棧邏輯
              
public void shouldPopSecondPushedValueFirst() throws Exception{
 stStack.push("test 1");
 stStack.push("test 2");
 Ensure.that(stStack.pop(), m.is("test 2"));
}
        清單 19 中的代碼可以按計(jì)劃運(yùn)行,所以我將實(shí)現(xiàn)另一個(gè)行為方法(在清單 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"));
}
        接下來(lái),我要確保 peek() 能按預(yù)期運(yùn)行。正如 Linda 所說(shuō),peek() 遵從和 pop() 相同的規(guī)則,但是 “應(yīng)該保留棧頂?shù)捻?xiàng)目”。相應(yīng)地,我在清單 21 中實(shí)現(xiàn)了 shouldLeaveValueOnStackAfterPeep() 方法的行為。


清單 21. 確保 peek 保留棧頂?shù)捻?xiàng)目
              
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() 還沒(méi)有定義,因此清單 21 還不能編譯。在清單 22 中,我定義了 peek() 的一個(gè)簡(jiǎn)單的實(shí)現(xiàn)。


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


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

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

棧為空的情況

        現(xiàn)在重新運(yùn)行 清單 21 中的行為,結(jié)果順利通過(guò)。但是,在這樣做的過(guò)程中發(fā)現(xiàn)一個(gè)問(wèn)題:如果棧為空,則 peek() 有怎樣的行為?如果說(shuō)棧為空時(shí)調(diào)用 pop() 會(huì)拋出一個(gè)異常,那么 peek() 是否也應(yīng)該如此?

        Linda 對(duì)此沒(méi)有進(jìn)行解釋,所以,顯然我需要自己添加新的行為。在清單 25 中,我為 “當(dāng)之前沒(méi)有調(diào)用 push() 時(shí)調(diào)用 peek() 會(huì)怎樣” 這個(gè)場(chǎng)景編寫了代碼。

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