开发者

What is the best testing pattern for checking that parameters are being used properly?

I'm using Rhino Mocks to try to verify that when I call a certain method, that the method in turn will properly group items and then call another method.

Something like this:

//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 ???

Here is where the trouble begins for me. My method is doing something like this:

public void HaveFunWithBucketsAndBalls(IList<IBall> balls)
{
    //group all the balls together according to color
    var blueBa开发者_JAVA技巧lls = 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
}

What I want to assert is that each time I call HaveFunWithABucketOfBalls that I'm calling it with a group of 1 red ball, then 1 blue ball, then 1 yellow ball, then 2 orange balls.

If I can assert that behavior then I can verify that the method is doing what I want it to do, which is grouping the balls properly.

Any ideas of what the best testing pattern for this would be?


One way to test this is to break out the responsibility of working with a colour-specific balls to a dependency, say, 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));

This test is then checking your BucketGame correctly calls HaveFunWithABucketOfBalls on the mocked object. This may still give you trouble in specifying what each argument should be. You can in turn make this easier to test (at the expense of introducing more abstraction) by pushing the responsibility for sorting the balls to a new dependency. You'd then end up with something like this:

//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));

And to pass this test, in 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);
    }
}

This tests the BucketGame logic. You'll now want to write unit tests for an IBallSorter implementation to check that it sorts the balls by colour as you require. Those tests probably won't need any mocking, you'll simply be able to throw data in and assert that the data you get back is what you expect.


the first thing you need to sort out in any test is what exactly your Subject Under Test is. that might sound gratuitous but I know from my own experiences that this can be confusing when you are learning interaction testing.

the reason I am guessing this is part of the problem is that you have an IBall object, but you want to asserts calls being made on a method that doesn't appear to be part of an interface. As you know, Rhino does it's magic by overriding something virtual, like an interface; so if you want to use to Rhino to do this you have to give it an interface. Assuming that is the case, maybe

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

Now you can set up an interaction test:

[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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜