views:

109

answers:

5

I have defined the following Unit-Test:

[TestMethod] //@Test for the Java crowd
public void In_The_Beginning_All_The_Board_Is_Black()
{
    IBoard board = new Board(new Size(10, 22));
    BoardEngine boardEngine = new BoardEngine(board);

    for (int y = 0; y < boardEngine.Size.Width; ++y)
    {
        for (int x = 0; x < boardEngine.Size.Width; ++x)
        {
            Assert.AreEqual<Color>(Color.Black, boardEngine.GetCellAt(new Point(x, y)).Color);
        }
    }
}

Now, the problem is that currently everything is simple and I can instantiate Board as it's shown, passing it its size in the constructor. I know later I'll have a way more complex Board, that has lots of dependencies, so I'd like to try to write out this test with a mock instead. The issue is that I don't get how. What should I setup with this mock?

The behaviour I want to test is that when a BoardEngine is instantiated, all the cells of the board must have its color set to black. How can I represent that in a test with a Mock instead of the Board class?

Internally, I currently just have an IBoard field in BoardEngine:

public class BoardEngine {
    IBoard board;

    ...

    public BoardEngine(IBoard board) {
        this.board = board;

        SetAllCellsToBlack();
    }


    private void SetAllCellsToBlack() {
        board.SetAllCellsTo(Color.Black);
    }

    ...
}

and IBoard is defined as follows:

public interface IBoard
{
    Size Size { get; }
    BoardCell GetCellAt(Point location);
    void SetCellAt(Point location, BoardCell cell);
    void SetAllCellsTo(Color color);
}

Am I taking the right approach here? Maybe the problem is that as BoardEngine is basically delegating all its work to IBoard, I should be testing an IBoard implementation and not BoardEngine?


Edit

So, if I'm understanding correctly what you guys are trying to tell me, I have 2 options:

  1. I can make (what I think would be, correct me if I'm wrong) Unit-Tests of both the BoardEngine and the Board. The BoardEngine's Unit-Test will just check whenever the call is being correctly implemented, using a Mock object of the Board. The Board's Unit-Test doesn't actually need a mock and just checks that when I run SetAllCellsTo(Color color) it will really turn them that color. With this, I am testing the behaviour.
  2. What I actually have, a test that tests the state after instantiating a boardEngine with a Board instance, checking all the board cells to see if they are black. Here, I am testing the state.

Is this correct?

Also, when using TDD, which tests should I do first? I'd say tests of type 1. When should I use tests of type 2?

Thanks

+1  A: 

This is going to vary depending on the mocking framework that you're using. With Rhino.Mocks, it would look like this:

var board = MockRepository.GenerateStub<IBoard>();
board.Size = new Size(2,2);

BoardEngine boardEngine = new BoardEngine(board);

board.AssertWasCalled(b => b.SetAllCellsTo(Color.Black),
     options => options.Repeat.Times(1));

Here you create the mock or stub, do what you're testing, and then assert that the expected thing happened. In this case, you're expecting SetAllCellsTo(Color.Black) to be called once.

You're not concerned with whether all the cells are now black, because that is behaviour outside of the unit -- outside, even, of the class under test, which in this case is BoardEngine. All you're testing is that BoardEngine calls the specified method on the injected dependency when it is instantiated.

edit in response to OP edit

The two options you outline in your edit are correct, but not mutually exclusive. I recommend that you have unit tests in place to be sure that each piece of functionality works as expected across various entry conditions, and tests that make sure the interaction works at a higher level.

Keep in mind that you can cover the bases by (1) testing that a call to board.SetAllCellsTo(Color.Black) actually works, independent of BoardEngine, and then (2) testing that BoardEngine calls that [tested to be working] method. Still, you should have higher-level tests to ensure that everything actually plays together as expected without side effects.

What test to start with is a bit subjective. In terms of driving out functionality with TDD, you need to have a good idea of how you want the system to work -- how BoardEngine and Board will work together. You could define both interfaces and outline a test, but you can't actually execute the test until you've implemented both interfaces, and if you write the test you won't be able to compile it because you'll have no classes to instantiate. On the other hand, you can write unit tests as you implement each interface. Start with IBoard because it has fewer dependencies. Start implementing Board : IBoard and cover it as you go. When you know that a call to SetAllColorsTo() works as expected, you'll be more comfortable with a unit test of the BoardEngine constructor calling that method.

Jay
About your last paragrah,t here's one thing bothering me. Let's say instead of calling the obvious SetAllCellsTo(Color.Black) I do it with a a sweep through all x and y values, calling for each one of them board.SetCellAt(x, y, Color.Whatever). That way the test would fail. Shouldn't the test test the behaviour(that is, that the Board object has all its cells set to Black) instead of the implementation(calling b.SetAllCells() or b.SetCellAt()) ? Thanks
devoured elysium
So maybe the test is telling you that it's time to add some kind of reset behaviour to the Board?
Steve Freeman
@devoured You said "The behaviour I want to test is that when a BoardEngine is instantiated, all the cells of the board must have its color set to black." Looking at the constructor of `BoardEngine`, it actually does not do what you say (call `SetCellAt(x,y,color)`). It calls the `SetAllCellsTo(color)`. Sweeping through the cells is a behaviour of an implementation of `IBoard`. Testing that would be a different test (of the `Board` class) and would, from what you've shown, not involve any stubs (because there are no dependencies).
Jay
@devoured If you really want to test that instantiating a `BoardEngine` sets the board cell colors to black, (1) you are no longer talking about a unit test because you are involving more than one implemented collaborator (which is fine, and allows you to test results) and (2) you wouldn't use any mocks/stubs because you are testing the behaviour of both `BoardEngine` and `Board`.
Jay
Yes, I think you got what my problem is in your last comment. The problem is that I don't understand well what I'm trying to accomplish. I am writing these tests TDD style, but I can't understand if they are really unit-tests or not. Until now, I have all my tests (about 30) without a single mock, but now I'm going to have to do a big refactoring to accomodate for my new tests that will test new more complex behaviour, and each of the current tests are getting a lot bigger because of that refactoring. That's why I thought maybe it should be better to put mocks on my first 30 tests.
devoured elysium
(continuation) so to cut part of the dependencies.
devoured elysium
@devoured If your tests are getting too big that's not a good sign. Sounds like there's room for refactoring the tests (possibly with mocks). You should avoid duplication not only in your production code but also in your tests. Otherwise you're going to end up with unmaintainable set of tests that you stop running once the burden of maintaining them gets too big. @Jay There can be a very heated debate what is and what is not a unit tests and different people have different opinions.
trendl
+2  A: 

i come from a java background and have zero .Net experience:

with typical mock libraries, you need to do the following things:

  1. create mock objects
  2. program the mock objects
  3. set the mock object to replay mode
  4. verify the mock object was used appropriately

i typically use the EasyMock library, and the above steps my look as follows in Java:

public class BoardEngineTest {
    @Test
    public void engineShouldSetCellsBlack() {

        // 1. create mock
        IBoard mockBoard = EasyMock.createMock(IBoard.class);

        // 2. program mock
        EasyMock.reset(mockBoard); // put in record mode

        // this doesn't actually happen now, the mock is just
        // being programmed to expect this method call with this
        // argument
        mockBoard.setAllCellsTo(Color.Black);

        // 3. put in replay mode - it's alive at this point!
        EasyMock.replay(mockBoard);

        // do something that cause the mock to be used
        BoardEngine engine = new BoardEngine(mockBoard);

        // 4. make sure cells were actually set to black!
        EasyMock.verify(mockBoard);
    }
}
LES2
Thanks. but what If I decide to call board.SetCellAt(x, y, Color.Black) for all x's and y's in the board, instead of calling SetAllCellsTo? The result would be correct but the test would fail. Shouldn't we be testing "results" instead of implementations?
devoured elysium
with mocks, you can enforce that the API of the mock is being used correctly; it's possible that iterating over all the cells and setting the color is 1,000 times slower; testing this way detects that (provided the tests are correct)
LES2
if you don't want to do this, i suppose you could inject a real implementation of a board that you could query after to test the results. then you could verify things in a high level way, e.g., iterate over the board and check that all cells are black. with mock libraries, you have to verify that the API is used correctly.
LES2
+2  A: 

The behavior of the board engine is different from the behavior of the board. Your board engine seems to be a facade, hiding away some complexity of the board. You're testing the board engine, so you should not test the behavior of the board, but that the engine calls the correct behavior of the board. In that case you inject a mock of the board and verify whether it has been called with how the board engine should call it.

koen
+2  A: 

You can approach this by verifying state or behaviour.
If you verify state you want to test that after BoardEngine is instatntiated all cells of it's board are black. In this case you would show that you do not care how it's achieved and you just want to test it's done.
Another option is to test behaviour by supplying a BoardEngine with a mock implementation of IBoard and after BoardEngine is instantiated you verify that the SetAllCellsTo method of IBoard was called with the appropriate color.
I'd prefer the first approach - testing state - if possible but sometimes you don't have a choice.

trendl
So from what I understood from your post when testing behaviour I (at least generally) can't have mocks, is that right?
devoured elysium
Now that I think of it, I could have mocks, but probably only in classes that go "deeper" than my main tested class and its dependencies.
devoured elysium
When you test behaviour you do use mocks as you only can verify everything went according to expectations by calling something like mockRepository.Verify() - This is RhinoMocks syntax.Have a look at http://martinfowler.com/articles/mocksArentStubs.html which is a very nice explanation of Status/behaviour testing.
trendl
I'm going to read it now. ty for the link.
devoured elysium
Yes, the article describes exactly the problem I was trying to understand.
devoured elysium
+3  A: 

The other thing to remember about this kind of TDD is that the functionality should be driven by an end-to-end test to make sure that the pieces all fit together. So, the unit test exercises the BoardEngine's relationship with its Board, driven by a higher-level test that drives the whole play.

Steve Freeman
Note that Steves book http://www.amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/0321503627 is a an excellent guide.
koen
I'm actually reading it. Although some things are bit over the top to me.
devoured elysium
@Steve : could you check, if possible, if my edit to the original post makes sense? ty
devoured elysium
Your analysis is correct. What's more interesting is whether you want to test the Board and Engine separately or not. For example, does anything else reference the Board? Or, you could write high-level tests of Engine with and embedded board, and write direct, more detailed tests of Board to fill in the gaps.There isn't a single answer. Try to think in terms of objects that collaborate, and protocols that define how they may talk to each other.
Steve Freeman
As for your actual question :), if you're just beginning then try it a couple of different ways. Make sure you go too far in each direction so that you get a sense of where the limits of a technique are.
Steve Freeman