Возникают проблемы с пониманием, как использовать Mock в модульном тесте - PullRequest
1 голос
/ 02 августа 2010

Я определил следующий юнит-тест:

[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, который имеет много зависимостей, поэтому я хотел бы попытаться выписать этот тест вместо макета. Проблема в том, что я не понимаю, как. Что мне настроить с этим макетом?

Поведение, которое я хочу проверить, заключается в том, что при создании экземпляра 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?


Редактировать

Итак, если я правильно понимаю, что вы, ребята, пытаетесь сказать мне, у меня есть 2 варианта:

  1. Я могу сделать (как мне кажется, поправить меня, если я ошибаюсь) модульные тесты BoardEngine и Board. Unit-Test BoardEngine будет просто проверять всякий раз, когда вызов выполняется правильно, используя Mock-объект платы. Unit-Test Совета на самом деле не нуждается в макете, а просто проверяет, что когда я запускаю SetAllCellsTo (Color color), он действительно превращает их в этот цвет. С этим я тестирую поведение.
  2. То, что у меня есть на самом деле, тест, который проверяет состояние после создания экземпляра BoardEngine с экземпляром Board, проверяя все ячейки Board на предмет их черного цвета. Здесь я проверяю состояние.

Это правильно?

Кроме того, при использовании TDD, какие тесты я должен делать в первую очередь? Я бы сказал, тесты типа 1. Когда я должен использовать тесты типа 2?

Спасибо

Ответы [ 5 ]

3 голосов
/ 02 августа 2010

Еще одна вещь, которую следует помнить об этом типе TDD, заключается в том, что функциональность должна определяться сквозным тестом, чтобы удостовериться, что все части соответствуют друг другу.Таким образом, юнит-тест осуществляет связь BoardEngine с его Board, основанную на высокоуровневом тесте, который управляет всей игрой.

2 голосов
/ 02 августа 2010

Вы можете подойти к этому, проверив состояние или поведение.
Если вы проверяете состояние, вы хотите проверить, что после создания BoardEngine все ячейки его платы черные. В этом случае вы покажете, что вам все равно, как это достигнуто, и вы просто хотите проверить, что это сделано.
Другой вариант - проверить поведение, предоставив BoardEngine фиктивную реализацию IBoard, и после создания экземпляра BoardEngine вы убедитесь, что метод SetAllCellsTo для IBoard был вызван с соответствующим цветом.
Я бы предпочел первый подход - тестирование состояния - если возможно, но иногда у вас нет выбора.

2 голосов
/ 02 августа 2010

Я пришел из Java-фона и не имею опыта работы с .Net:

с типичными библиотеками-макетами, вам нужно сделать следующее:

  1. создать объекты-макеты
  2. запрограммируйте фиктивные объекты
  3. установите для имитируемого объекта режим воспроизведения
  4. убедитесь, что фиктивный объект был использован надлежащим образом

Обычно я использую библиотеку 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);
    }
}
2 голосов
/ 02 августа 2010

Поведение игрового движка отличается от поведения игрового поля. Похоже, ваш движок доски фасад , скрывающий некоторую сложность доски. Вы тестируете движок доски, поэтому вам следует проверять не поведение доски, а то, что движок вызывает правильное поведение доски. В этом случае вы вводите макет доски и проверяете, была ли она вызвана так, как ее должен называть механизм доски.

2 голосов
/ 02 августа 2010

Это изменится в зависимости от используемой вами модели. С 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 вызывает указанный метод для внедренной зависимости, когда она создается.

редактировать в ответ на OP edit

Два параметра, которые вы наметили при редактировании, верны, но не являются взаимоисключающими. Я рекомендую, чтобы у вас были модульные тесты, чтобы убедиться, что каждый компонент функционирует должным образом при различных начальных условиях, и тестах, которые обеспечивают взаимодействие на более высоком уровне.

Имейте в виду, что вы можете покрыть базы, (1) проверяя, что вызов board.SetAllCellsTo(Color.Black) действительно работает, независимо от BoardEngine, и затем (2) проверяя, что BoardEngine вызывает, что [проверено на работоспособность ] метод. Тем не менее, у вас должны быть высокоуровневые тесты, чтобы убедиться, что все действительно играет вместе, как и ожидалось, без побочных эффектов.

Какой тест для начала немного субъективен. С точки зрения реализации функциональности с помощью TDD вам необходимо иметь четкое представление о том, как вы хотите, чтобы система работала - как BoardEngine и Board будут работать вместе. Вы можете определить оба интерфейса и наметить тест, но вы не сможете выполнить тест до тех пор, пока не реализуете оба интерфейса, и если вы напишете тест, вы не сможете его скомпилировать, потому что у вас не будет классов для экземпляр. С другой стороны, вы можете написать модульные тесты при реализации каждого интерфейса. Начните с IBoard, потому что у него меньше зависимостей. Начните внедрять Board : IBoard и покройте это, как вы идете. Если вы знаете, что вызов SetAllColorsTo() работает должным образом, вам будет удобнее с модульным тестом конструктора BoardEngine, вызывающего этот метод.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...