2010-04-12 50 views
1

我使用Rhino Mocks嘗試驗證當我調用某個方法時,該方法反過來將正確地分組項目,然後調用另一種方法。檢查參數是否正確使用的最佳測試模式是什麼?

事情是這樣的:

//Arrange 
var bucketsOfFun = new BucketGame(); 

var balls = new List<IBall> 
       { 
        new Ball { Color = Color.Red }, 
        new Ball { Color = Color.Blue }, 
        new Ball { Color = Color.Yellow }, 
        new Ball { Color = Color.Orange }, 
        new Ball { Color = Color.Orange } 
       }; 

//Act 
bucketsOfFun.HaveFunWithBucketsAndBalls(balls); 

//Assert ??? 

這裏就是麻煩的開始我。我的方法是做這樣的事情:

public void HaveFunWithBucketsAndBalls(IList<IBall> balls) 
{ 
    //group all the balls together according to color 
    var blueBalls = GetBlueBalls(balls); 
    var redBalls = GetRedBalls(balls); 
    // you get the idea 

    HaveFunWithABucketOfBalls(blueBalls); 
    HaveFunWithABucketOfBalls(redBalls); 
    // etc etc with all the different colors 
} 

public void HaveFunWithABucketOfBalls(IList<IBall> colorSpecificBalls) 
{ 
    //doing some stuff here that i don't care about 
    //for the test i'm writing right now 
} 

我想斷言的是,每次我打電話說我有一組1個紅球,那麼1個藍色球調用它HaveFunWithABucketOfBalls,然後1個黃球,然後2個橙色的球。

如果我可以斷言該行爲,那麼我可以驗證該方法正在做我想做的事情,即正確地分組這些球。

任何想法是什麼樣的最好的測試模式?

+0

只是爲了方便,爲什麼你測試你的方法調用另一種方法?據推測,你應該只是測試你的對象的公共方法,並斷言你得到正確的輸出,你不應該測試實際的內部實現。 – Juliet 2010-04-12 23:15:18

+0

@Juliet這是一個很好的觀點,答案是因爲這兩種方法都是公開的。在我的實際實現中,它們都可以獨立使用。所以兩者之間唯一真正的區別是對象如何從我在這裏描述的方法組織起來,這就是爲什麼我想測試這個功能。 – Joseph 2010-04-13 17:16:51

回答

2

一種方法是打出來的責任與顏色特定的球一起工作的依賴,例如,IBucketHandler

//Arrange 
var bucketHandler = MockRepository.GenerateStub<IBuckerHandler>(); 
var bucketsOfFun = new BucketGame(bucketHandler); 
... 
//Assert 
bucketHandler.AssertWasCalled(x => x.HaveFunWithABucketOfBalls(redBalls)); 
bucketHandler.AssertWasCalled(x => x.HaveFunWithABucketOfBalls(greenBalls)); 

那麼這個測試是檢查你BucketGame正確調用HaveFunWithABucketOfBalls的嘲笑對象。這可能仍然會在指定每個參數應該是什麼時遇到麻煩。反過來,通過將排序球的責任推給新的依賴關係,您可以更容易地進行測試(以引入更多抽象爲代價)。那麼你最終的東西是這樣的:

//Arrange 
var balls = new List<IBall>(); //Can really be anything, we just need the object reference 
var greenBalls = new List<IBall>(); 
var redBalls = new List<IBall>(); 
var sortedBalls = new [] { greenBalls, redBalls }; 

var bucketHandler = MockRepository.GenerateStub<IBucketHandler>(); 
var ballSorter = MockRepository.GenerateStub<IBallSorter>(); 
ballSorter.Stub(x => x.Sort(balls)).Return(sortedBalls); 

var bucketsOfFun = new BucketGame(bucketHandler, ballSorter); 

//Act 
bucketsOfFun.HaveFunWithBucketsAndBalls(balls); 

//Assert 
bucketHandler.AssertWasCalled(x => x.HaveFunWithABucketOfBalls(greenBalls)); 
bucketHandler.AssertWasCalled(x => x.HaveFunWithABucketOfBalls(redBalls)); 

而且通過這個測試,在BucketGame

public BucketGame(IBucketHandler bucketHandler, IBallSorter ballSorter) 
{ 
    this.bucketHandler = bucketHandler; 
    this.ballSorter = ballSorter; 
} 

public void HaveFunWithBucketsAndBalls(IList<IBall> balls) 
{ 
    //group all the balls together according to color 
    var sortedBalls = ballSorter.Sort(balls); 
    foreach (var groupOfBalls in sortedBalls) 
    { 
     bucketHandler.HaveFunWithABucketOfBalls(groupOfBalls); 
    } 
} 

此測試BucketGame邏輯。您現在要爲IBallSorter實現編寫單元測試,以檢查它是否按照需要按顏色對球進行排序。這些測試可能不需要任何嘲諷,你只需要能夠拋出數據,並斷言你獲得的數據是你期望的。

+0

謝謝大衛。我認爲這對我來說可能是最好的方法。有趣的是,我真正的實現類似於這種構造,我只需要考慮一個名爲BucketHandler的抽象名稱。 – Joseph 2010-04-13 17:23:26

+0

一個問題,如果BucketHandler是一個存根或一個模擬,因爲你斷言該對象已經做了一些事情,這讓我認爲它應該是一個模擬,而不是一個存根,但在你的例子中,你打電話MockRepository.GenerateStub (); ? – Joseph 2010-04-13 17:26:20

+0

@joseph - 將它作爲存根是我習慣的一種習慣。我幾乎從不使用GenerateMock ,因爲我通常希望我的屬性作爲實際屬性(例如myStub.SomeProp = 10;)工作。在這種情況下,您可以用GenerateMock 替換它,它仍然可以工作,並且可能更符合語義。 – 2010-04-13 21:45:33

0

你需要在任何測試中挑出的第一件事就是你的受試者是什麼。這可能聽起來沒有道理,但我從我自己的經驗中知道,當你學習交互測試時,這可能會令人困惑。

我猜測這是問題的一部分的原因是你有一個IBall對象,但是你想要聲明一個方法看起來並不是接口的一部分。正如你所知道的,犀牛通過重寫虛擬的東西來實現它,比如一個界面;所以如果你想用Rhino來做到這一點,你必須給它一個接口。假如是這樣的話,也許

interface IBucketGame{ 
    void HaveFunWithBucketsAndBalls(IList<IBall> balls) 
    void HaveFunWithSpecificBalls(IList<IBall> balls) 
} 

現在,您可以建立一個互動測試:

[Test] 
public void HaveFunWithBucket_IfMoreThanOneColor_CallsHaveFunWithSpecificBallsForSpecificColor() 
    { 
     //Arrange 
     var bucketsOfFun = MockRepository.GenerateMock<IBucketGame>(); 

     IBall expColor_1 = new Ball(Color.Red); 
     IBall expColor_2 = new Ball(Color.Green); 
     IBall expColor_3 = new Ball(Color.Red); 
     var startlist = new List<IBall>{expColor_1, expColor_2, expColor_3}; 
     var onlyRedBalls = new List<IBall>{expColor_1, expColor_3}; 
     var onlyGreenBalls = new List<IBall>{expColor_2}; 

     //Act 
     bucketsOfFun.HaveFunWithBucketsAndBalls(balls); 

     //Assert 
     bucketsOfFun.AssertWasCalled(x=>x.HaveFunSpecificBalls(Arg<IEnumerable<IBall>>.List.Equal(onlyRedBalls))); 
     bucketsOfFun.AssertWasCalled(x=>x.HaveFunSpecificBalls(Arg<IEnumerable<IBall>>.List.Equal(onlyGreenBalls))); 

    } 

HTH,
Berryl測試這個

+0

@Berryl然後,你如何測試,調用bucketsOfFun.HaveFunWithBucketsAndBalls(球)實際上什麼都做。你只是嘲笑一個沒有定義實現的接口。我試圖測試一個具體的實現,所以我有點失去你在那裏得到的東西。 – Joseph 2010-04-12 22:52:49

+0

這是一個無意義的測試,但這就是你當前的規格要求!我以爲你只是想看看犀牛的一些機制,所以你可以開始思考一個邏輯用例,這將導致邏輯​​測試... – Berryl 2010-04-13 00:12:53

+0

沒有我問的問題只是一個代理真實存在的問題。實際上我有一個用例想要做到這一點,我無法弄清楚測試它的方法。所以爲了澄清,我的問題不是假設,而是我試圖克服的一個實際問題。 – Joseph 2010-04-13 17:18:21

相關問題