C# 在理解如何在单元测试中使用Mock时遇到困难

C# 在理解如何在单元测试中使用Mock时遇到困难,c#,java,unit-testing,mocking,C#,Java,Unit Testing,Mocking,我定义了以下单元测试: [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.

我定义了以下单元测试:

[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);
        }
    }
}
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


编辑 所以,如果我正确理解你们想告诉我的话,我有两个选择:

  • 我可以对BoardEngine和Board进行单元测试(我认为是,如果我错了,请纠正我)。BoardEngine的单元测试只会在调用正确实现时使用Board的模拟对象进行检查。电路板的单元测试实际上不需要模拟,只是检查当我运行SetAllCellsTo(颜色)时,它是否真的会将它们变成那种颜色。有了这个,我正在测试这种行为
  • 我实际上有一个测试,在用一个板实例实例化一个boardEngine之后测试状态,检查所有的板单元是否为黑色。在这里,我正在测试状态
  • 这是正确的吗

    另外,在使用TDD时,我应该首先进行哪些测试?我认为是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));
    
    var board=MockRepository.GenerateStub();
    板尺寸=新尺寸(2,2);
    BoardEngine BoardEngine=新的BoardEngine(板);
    调用board.assertwas(b=>b.SetAllCellsTo(Color.Black),
    options=>options.Repeat.Times(1));
    
    在这里,您创建模拟或存根,执行您正在测试的操作,然后断言预期的事情发生了。在本例中,您希望调用一次
    SetAllCellsTo(Color.Black)

    您不关心是否所有的单元格现在都是黑色的,因为这是单元之外的行为——甚至是被测类之外的行为,在本例中是
    BoardEngine
    。您要测试的是
    BoardEngine
    在注入依赖项实例化时调用该依赖项上的指定方法

    编辑以响应OP edit 您在编辑中列出的两个选项是正确的,但不是相互排斥的。我建议您进行单元测试,以确保每项功能在各种进入条件下都能按预期工作,并进行测试以确保交互在更高级别上工作

    请记住,您可以通过(1)测试对
    board.SetAllCellsTo(Color.Black)
    的调用是否实际工作,独立于
    BoardEngine
    ,然后(2)测试
    BoardEngine
    调用[tested to be working]方法。尽管如此,您还是应该进行更高级别的测试,以确保所有内容都按照预期进行,并且没有副作用


    从什么测试开始有点主观。在使用TDD实现功能方面,您需要对系统的工作方式有一个很好的了解--
    BoardEngine
    Board
    将如何协同工作。您可以定义这两个接口并概述一个测试,但在实现这两个接口之前,您无法实际执行测试,如果编写测试,您将无法编译它,因为您将没有可实例化的类。另一方面,您可以在实现每个接口时编写单元测试。从
    IBoard
    开始,因为它具有较少的依赖项。开始执行Board:IBoard,并在执行过程中覆盖它。当您知道对
    SetAllColorsTo()
    的调用按预期工作时,您将更熟悉调用该方法的
    BoardEngine
    构造函数的单元测试。

    这将根据您使用的模拟框架而有所不同。使用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));
    
    var board=MockRepository.GenerateStub();
    板尺寸=新尺寸(2,2);
    BoardEngine BoardEngine=新的BoardEngine(板);
    调用board.assertwas(b=>b.SetAllCellsTo(Color.Black),
    options=>options.Repeat.Times(1));
    
    在这里,您创建模拟或存根,执行您正在测试的操作,然后断言预期的事情发生了。在本例中,您希望调用一次
    SetAllCellsTo(Color.Black)

    您不关心是否所有的单元格现在都是黑色的,因为这是单元之外的行为——甚至是被测类之外的行为,在本例中是
    BoardEngine
    。您要测试的是
    BoardEngine
    在注入依赖项实例化时调用该依赖项上的指定方法

    编辑以响应OP edit 您在编辑中列出的两个选项是正确的,但不是相互排斥的。我建议您进行单元测试,以确保每项功能在各种进入条件下都能按预期工作,并进行测试以确保交互在更高级别上工作

    请记住,您可以通过(1)测试对
    board.SetAllCellsTo(Color.Black)
    的调用是否实际工作,独立于
    BoardEngine
    ,然后(2)测试
    BoardEngine
    调用[tested to be working]方法。尽管如此,您还是应该进行更高级别的测试,以确保所有内容都按照预期进行,并且没有副作用

    从什么测试开始有点主观。在使用TDD实现功能方面,您需要对系统的工作方式有一个很好的了解--
    BoardEngine
    Board
    将如何协同工作。您可以定义这两个接口并概述一个测试,但在实现这两个接口之前,您无法实际执行测试,如果编写测试,您将无法编译它,因为您将没有可实例化的类。另一方面,您可以在实现每个接口时编写单元测试。Sta
    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);
        }
    }