我定义了以下单元测试:
[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);
}
}
}
现在,问题是目前一切都很简单,我可以实例化显示的Board
,并在构造函数中传递它的大小。我知道后来我会有更复杂的Board
,它有很多依赖,所以我想尝试用mock来编写这个测试。问题是我没有得到如何。我应该用这个模拟器设置什么?
我想测试的行为是,当实例化BoardEngine
时,电路板的所有单元格必须将其颜色设置为黑色。如何在Mock而不是Board类的测试中表示这一点?
在内部,我目前在IBoard
中只有一个BoardEngine
字段:
public class BoardEngine {
IBoard board;
...
public BoardEngine(IBoard board) {
this.board = board;
SetAllCellsToBlack();
}
private void SetAllCellsToBlack() {
board.SetAllCellsTo(Color.Black);
}
...
}
和IBoard
定义如下:
public interface IBoard
{
Size Size { get; }
BoardCell GetCellAt(Point location);
void SetCellAt(Point location, BoardCell cell);
void SetAllCellsTo(Color color);
}
我在这里采取了正确的方法吗?也许问题在于BoardEngine
基本上将其所有工作委托给IBoard
,我应该测试一个IBoard
实现而不是BoardEngine
?
所以,如果我正确理解你们要告诉我的内容,我有两个选择:
这是对的吗?
另外,使用TDD时,我应该先做哪些测试?我会说类型1的测试。我什么时候应该使用类型2的测试?
由于
答案 0 :(得分:3)
关于这种TDD需要记住的另一件事是,功能应该由端到端测试驱动,以确保所有部分都适合。因此,单元测试演练了BoardEngine与其董事会的关系,这是由推动整场比赛的更高级别测试所驱动的。
答案 1 :(得分:2)
这取决于您正在使用的模拟框架。使用Rhino.Mocks,它看起来像这样:
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));
在这里,您可以创建模拟或存根,执行您正在测试的内容,然后断言预期的事情发生了。在这种情况下,您希望调用SetAllCellsTo(Color.Black)
一次。
你不关心所有的细胞现在都是黑色的,因为这是在单位之外的行为 - 甚至在被测试的类之外,在这种情况下是BoardEngine
。您正在测试的是BoardEngine
在实例化时对注入的依赖项调用指定的方法。
您在编辑中列出的两个选项是正确的,但不是互斥的。我建议你进行单元测试,以确保每个功能在各种输入条件下正常工作,和测试确保交互工作在更高的层次。
请注意,您可以通过以下方式覆盖基础:(1)测试board.SetAllCellsTo(Color.Black)
的呼叫是否真的有效,独立于BoardEngine
,然后(2)测试BoardEngine
来电[测试工作]方法。不过,您应该进行更高级别的测试,以确保所有内容实际上按预期一起播放而没有副作用。
开始时的测试有点主观。在使用TDD驱逐功能方面,您需要很好地了解系统的工作方式 - BoardEngine
和Board
如何协同工作。您可以定义两个接口并概述测试,但是在实现两个接口之前实际上不能执行测试,如果编写测试,则无法编译它,因为您没有类实例。另一方面,您可以在实现每个接口时编写单元测试。从IBoard
开始,因为它具有较少的依赖性。开始实施Board : IBoard
并随时覆盖它。当您知道对SetAllColorsTo()
的调用按预期工作时,您会更熟悉调用该方法的BoardEngine
构造函数的单元测试。
答案 2 :(得分:2)
我来自java背景,没有.Net经验:
使用典型的模拟库,您需要执行以下操作:
我通常使用EasyMock库,上面的步骤在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);
}
}
答案 3 :(得分:2)
电路板引擎的行为与电路板的行为不同。您的电路板引擎似乎是a facade,隐藏了一些复杂的电路板。您正在测试电路板引擎,因此您不应该测试电路板的行为,而是引擎会调用电路板的正确行为。在这种情况下,你注入了一个模板,并验证它是否已被调用板引擎应如何调用它。
答案 4 :(得分:2)
您可以通过验证状态或行为来解决此问题
如果您在BoardEngine实例化后验证状态是否需要测试,那么它的所有单元都是黑色的。在这种情况下,你会表明你不关心它是如何实现的,你只是想测试它已经完成
另一个选择是通过向BoardEngine提供IBoard的模拟实现来测试行为,并在实例化BoardEngine之后验证是否使用适当的颜色调用了IBoard的SetAllCellsTo方法。
我更喜欢第一种方法 - 测试状态 - 如果可能的话,但有时候你没有选择。