views:

95

answers:

4

I have a bunch of tests that assume that my Tetris class is composed by a Board class. I now feel that instead of having a Board inside of that Tetris class, what I will want is to have a Board inside a BoardEngine class that is inside the Tetris class. That is what this test is asking for:

    [TestMethod]
    public void Start_Game_And_Check_That_A_Red_Cube_Appears_And_Moves_Down_One_Pixel_On_First_Iteration() {
        Board board = new Board(10, 22);
        BoardEngine boardEngine = new BoardEngine(board);
        Tetris tetris = new Tetris(boardEngine);

        //test that the pixels are black/empty at first
        Assert.AreNotEqual(Color.Red, tetris.GetColorAt(0, 0));
        Assert.AreNotEqual(Color.Red, tetris.GetColorAt(1, 0));

        tetris.Start();
 //and that after running Start() the pixels are set to red
        Assert.AreEqual(Color.Red, tetris.GetColorAt(0, 0));
        Assert.AreEqual(Color.Red, tetris.GetColorAt(1, 0));
    }

So to run this code I had to first create a BoardEngine class. After that, I need an empty BoardEngine constructor that accepts a Board as argument. I then need to create an empty new constructor on Tetris that accepts a BoardEngine as argument.

If I try running the code, I will get a NullPointerException. Why? Because when trying to do

tetris.GetColorAt(0, 0)

since in this constructor I am using now I haven't set the Board in Tetris to anything, it will just blow up.

My first question arises here. What to do now? In one hand, I can make this don't crash by setting that Board to something. On the other hand, what I really want is not to set it to anything, what I'll want is to eventually get rid of it, so my Tetris class only has a BoardEngine attribute.

Am I supposed to go and refactor the tests themselves? I guess it's the only way of making this work. Am I missing something? At which time should I refactor the other tests? If I refactor them BEFORE trying to run this test, I then can mantain all those other tests green while this one is red. On the other hand, if I try to make this one be green asap, all the others are going turn to red :(

Here is an example of an old test:

[TestMethod]
public void ...() {
    Board board = new Board(10, 22);
    Tetris tetris = new Tetris(board);

    tetris.Start();
    Assert.AreEqual(Color.Red, board.GetColorAt(0, 0));
    Assert.AreEqual(Color.Red, board.GetColorAt(1, 0));
}

Thanks

A: 

Sounds like you should look at a mocking framework for testing purposes; try Mockito - http://mockito.org/

e.g.

Board board = mock(Board.class);
BoardEngine boardEngine = new BoardEngine(board);
Tetris tetris = new Tetris(boardEngine);
when(board.GetColorAt(0, 0)).thenReturn(Color.RED);
Assert.AreEqual(Color.Red, tetris.GetColorAt(0, 0));

It sounds as though you are trying to maintain tests as passing during a refactor (substituting board for boardEngine). If you are following Test Driven Development then you should update the tests so that all tests are failing; then update your code with the proposed changes.

In practice using a framework such as Mockito allows you to separate your concerns. I.e. you can test the expected behavior of the Tetris class without making the test depend on the behavior of the BoardEngine/Board classes.

Syntax
I thought the idea was doing one test at a time, and the idea would be that all older tests are always green. am i wrong?
devoured elysium
My big question is: in the moment I recognized I needed to do a refactor, should I go first and edit all my older tests so they fit with the refactor, or should I design a new test that "asks" for this refactoring (where I introduce the new classes, etc)?
devoured elysium
Updated the answer to include the approach which should be used in Test Driven Development. It sounds like you are having problems with code separation/separation of concern.
Syntax
let's start by this "If you are following Test Driven Development then you should update the tests so that all tests are failing;". If I update all the tests at once so they now they expect my refatorored code(still not implemented!) such they are failing, then ain't I losing the "bullet-proof vest" I had by writing them in the first place?
devoured elysium
Your tests should verify the expected behavior of your classes. The abnormally large amount of rework resulting from this refactor indicates poor separation of concerns. Plainly, it should be possible for you to refactor this class without having to break EVERY test, it should only break the tests which directly test this class. In this way the 'bullet proof vest' remains functional (i.e. your tests verify all parts of the system), the only broken tests are those which will pass once the refactor is complete. I may have been unclear above, by 'all' I meant all tests affected by the refactor.
Syntax
Thing is, doing TDD I started with only the minimum work possible to get the job done. That was having only a Tetris class. Later I learned that I needed to have a Board class. And only know I learnt that I needed a BoardEngine class. I don't get how am I supposed to do this without breaking the tests.
devoured elysium
Well, it is breaking all the tests because all the tests until now mostly deal with setting a board and checking everything is there.
devoured elysium
A: 

From my pov this is not à problemen with the tests.

The method GetColorAt in the tetris class looks like à wrapper on the same method in the board class. The instance of the board-class has however moved from the tetris-class to the boardengine. Either expose the board from the engine as a property so the tetris class has access to it, or create a GetColorAt method on the engine to chain the call from tetris through the engine to the board.

Class Tetris{
    public Color GetColorAt(int x, int y){
        return  _engine.Board.GetColorAt(x,y);
    }
}

Or

Class Tetris{
    Public Color GetColorAt(int x, int y){
        return  _engine.GetColorAt(x,y);
    }
}

Class Boardengine{
    Public Color GetColorAt(int x, int y){
        return  _board.GetColorAt(x,y);
    }
}
lboshuizen
I think you are not getting what my trouble is. Of course I could just do what you are saying, but generally the idea with TDD would be to have to make tests that "ask" for a specific implementation. The implementation should be the result of the tests.
devoured elysium
Your tests demands for a layout and nesting of classes. Not for the coding inside your methods. An object has moved but you still need its use. How you do that is not a concern of the tests.
lboshuizen
+2  A: 

In general, if you change the design, you will have to change your tests as well. In this case, because your constructor is evolving, and taking a new concrete type of dependency, you will need to refactor the tests.

Part of the pains here is that you are taking a class as a dependency; if you were taking an IBoard, you could both use mocking more easily, like Syntax is suggesting, and have a test that doesn't depend on a concrete implementation. You could also probably refactor more easily your code, because you could proceed in 2 phases: add the interface contract to the new class you want to use, and then progressively remove it from the old class. You could actually start this approach right now: first extract Board into an interface, then implement the interface on BoardManager and remove it from Board.

That being said, sometimes when you are on solid ground, it's just plain easier and faster to just have a bunch of tests go red together and fix them all at once!

Another remark: your issue might also indicate that you are not unit testing the right class. If GetColor is a function provided by Board or BoardManager, maybe your unit test should be on that class only, and the Tetris class should simply check that it has a BoardManager available to call, and that it is relaying the calls properly to it...

Mathias
You do raise a lot of good points. About your last paragraph, I think I'm getting a bit confused about what should tests test. I mean, there should be acceptance tests(those are what I'd like to start with), and there are unit-tests. The method I show above would fall in which terrain? And why?
devoured elysium
Good question. Your last snippet suggests GetColor is a Board method, so there is no question for me that your "old" test should be a unit test on Board. A unit test on Tetris should check that when Tetis.GetColor is called, the proper arguments are passed to Board, or BoardManager, and not worry about what "answer" that class gives back. This is not the responsibility of Tetris, it is the job of Board. The more focused your unit tests are on the job of the class, the easier refactorings are. Testing the whole system together will be more brittle because it depends on the whole wiring.
Mathias
This comment from Mathias states clearly what I am trying (and perhaps failing) to state below, Upvoting.
Syntax
I do understand that unit-tests are all about testing a specified class alone, mocking/faking other classes when needed. What I don't get is how to get the DRIVEN part of test-driven-development into account. I could just do the Tetris game design upfront and then just create the classes and unit-test them in separation. But what I want to learn to do is see the design evolve doing a test at a time.
devoured elysium
That is the reason I didn't create an IBoard (yet). I just needed a Board, and that was the mininum amount of code I needed to get my current tests run.
devoured elysium
I think what you did so far is fine. The dynamics of TDD is red, green, refactor. You are reaching a point where you need to refactor. You could get away with not extracting an interface, and approach refactoring from another angle; I would go that route because I think it will make your refactoring easier, and also because in general, it makes it easier to deal with modifying code / relationships when your design is still evolving.
Mathias
+2  A: 

This code:

Board board = new Board(10, 22);
Tetris tetris = new Tetris(board);

will still work fine, if you create a constructor for Tetris that takes a Board, creates a BoardEngine around it, and calls the BoardEngine constructor. Then you can inline that constructor, and Poof! your Board disappears from the Tetris interface - and all your existing tests still work. This counts as refactoring while green, as far as I'm concerned.

Carl Manaster
Hmm good one. I was also thinking that instead of passing that kind of dependencies through the constructor, it'd be better just have a public constructor that instantiates the class to the default values and then have internal properties for each one of the dependencies that I can set if I want. Looks way less troublesome this way.
devoured elysium