views:

90

answers:

3

This question comes a bit as continuation of the following topic (you don't need to read it, though). It's just a game of Tetris I'm implementing with TDD.

So here is the problem: I have a set of Acceptance tests. I have defined the following test in one of them:

[TestMethod]
public void I_Can_Query_Any_Piece_Of_The_Board_For_A_Color() {
    StandardTetris tetris = new StandardTetris();

    for (int y = 0; y < tetris.BoardSize.Height; ++y) {
        for (int x = 0; x < tetris.BoardSize.Width; ++x) {
            Color color = tetris.GetColorAt(x, y);

            Assert.IsTrue(
                color == Color.Cyan ||
                color == Color.Blue ||
                color == Color.Orange ||
                color == Color.Yellow ||
                color == Color.Green ||
                color == Color.Violet ||
                color == Color.Red
                );
        }
    }
}

which made me change a method I had on StandardTetris

public Color GetColorAt(int x, int y)
{
    return Color.Black;
}

to

public Color GetColorAt(int x, int y)
{
    return Color.Orange;
}

The next test I'd like to do on was setting a couple of pixels to some colors, and then checking if they are indeed with that color in the positions I put them(although, now that I think of it, that would not be acceptance tests). How can I do that? StandardTetris doesn't provide any kind of setter for the board pieces(not it is supposed to do it!) and I don't want to have any other constructor than the default one. How can I mock it, then?


This is the current code on StandardTetris:

public class StandardTetris
{
    private static readonly int BOARD_WIDTH = 10;
    private static readonly int BOARD_HEIGHT = 22;

    private Size boardSize = new Size(BOARD_WIDTH, BOARD_HEIGHT);

    public Size BoardSize { get { return boardSize; } }

    public Color GetColorAt(int x, int y)
    {
        return Color.Orange;
    }
}

and by using your suggestions, I made the following test:

    [TestMethod]
    public void Set_A_Blue_2x2_Square_On_Origin_And_Query_It_Sucessfully() {
        Board board = new Board();
        board.SetColorAt(0, 0, Color.Blue);
        board.SetColorAt(0, 1, Color.Blue);
        board.SetColorAt(1, 0, Color.Blue);
        board.SetColorAt(1, 1, Color.Blue);

        Tetris tetris = new Tetris(board);

        Assert.AreEqual(Color.Blue, tetris.GetColorAt(0, 0));
        Assert.AreEqual(Color.Blue, tetris.GetColorAt(1, 0));
        Assert.AreEqual(Color.Blue, tetris.GetColorAt(0, 1));
        Assert.AreEqual(Color.Blue, tetris.GetColorAt(1, 1));
    }
A: 

I would have used an IoC library to insert the Board instance into the StandardTetris instance through the constructor. Then, in the test methods you can manually construct a StandardTetris instance with a mocked board which will return whatever you like.

public class StandardTetris
{
  IBoard _theBoard;
  public StandardTetris(IBoard boardInstance)
  {
    _theBoard = boardInstance;
  }
}

Then in the test methods:

StandardTetris tetris = new StandardTetris(new MockBoard());

In the runtime code you would just pass this off to the IoC library after having registered the IBoard interface as resolving the the runtime board class.:

StandardTestris tetris = new IoCContainer.Resolve<StandardTetris>();
Dr Herbie
I don't want to use IoC Containers or anything like that. Right now I have one problem, learning how to do TDD. If I put IoC on the mix, I end up having 2 problems: learning TDD and how to use IoC containers. Edit: I'm not saying it's hard, it's just that I want to learn to deal with TDD without frameworks, and only after I will start using them.
devoured elysium
Then I would learn IoC *first* because it is an incredibly useful tool to have for TDD (for me TDD is the main reason I use Ioc).
Dr Herbie
+1  A: 

Pixels seems very low level to me. If we're talking about pieces on a board I would expect to test that there is a shape on the board at a particular grid position. As we're dealing with the board I would write tests against that so I would do the following for the board:

1) Test that there is a shape on the board at a particular position e.g. (TShape refers to that T shape in tetris, hope that's clear ;) )

  public void TestHasTShapeAtCoordinates()
    Assert.isEqual(board.shapeAt(1,2), TShape.new)
  end

2) Test that I can position a tshape

opsb
Sorry, when I said pixels I meant pieces on board.
devoured elysium
I don't pretend to test the GUI right now. But I think you didn't get what the problem is. The problem is how to SET the T on the game's board, in the first place.
devoured elysium
I edited the original post with the current code of StandardTetris. Let's forget the other thread(where I implemented the Board class). Let's assume right now I just have this.
devoured elysium
OK, assuming that you're testing against StandardTetris instead of Board you would write tests 1 and 2 that I mention above. I haven't used the terms getter and setter but that's what they are. Test 1 checks that you can read the position (getter) and test 2 checks that you can position a shape(setter).
opsb
+1  A: 

I see two options:

  • Create a second constructor where you can pass in pre-constructed information.

  • Create a test class TestStandardTetris that inherits from StandardTetris where the only difference is TestStandardTetris has the constructor for taking the pre-constructed information.

Your tests are saying to you that you need a way to set the state, and you're saying you don't want the state to change, hence the lack of a setter, so the only place left to set the state is the constructor.

The first option opens it up to the game's main API.

The second only opens it up to tests, and any future person who works with your API who decides to inherit from StandardTetris, it will also allow you test the functionality of StandardTetris if the only thing that's changed is adding the constructor to TestStandardTetris

There are probably other and better ways of doing this, so wait to see if any one else comes up with a better answer. :)

Sekhat
Ok, I don't have any problem at all with having to pass things in the constructor(I will define it as internal, so it can only be accessed from the same assembly, so the end user will just see one available constructor), it's just that I thought there would be other ways of doing this. But it seems everyone is pointing in the same direction.
devoured elysium
I edited the original post, does that test look good to you? ty
devoured elysium