How to Test façade classes with no args constructors and no setters with TDD?
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
);
}
}
}
开发者_StackOverflowwhich 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));
}
I see two options:
Create a second constructor where you can pass in pre-constructed information.
Create a test class
TestStandardTetris
that inherits fromStandardTetris
where the only difference isTestStandardTetris
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. :)
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
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>();
精彩评论