Фаза рефакторинга светофора TDD - как это сделать правильно? - PullRequest
2 голосов
/ 25 июля 2010

Итак, я сделал следующий тест для класса Board, который должен был родиться:

[TestMethod]
public void Set_The_Origin_As_Violet_And_The_Query_Confirms_It() {
    Board board = new Board(10, 10);

    Color expected = Color.Violet;
    board.SetColorAt(0, 0, expected);
    Color actual = board.GetColorAt(0, 0);
    Assert.AreEqual(expected, actual);
}

Я попытался запустить код, но компилятор дал понять, что Board не существует. И я его создал.

Я попытался запустить код еще раз, но он оказался бесполезным, так как методов SetColorAt () и GetColorAt () не было. Я создал их:

public void SetColorAt(int x, int y, Color color) {
}

public void GetColorAt(int x, int y) {
}

Тем не менее, не все было хорошо, так как мне пришлось вернуть Color.Violet. Поэтому я изменил GetColorAt () на

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

Итак, я впервые получил зеленый свет.

То, что я хочу, чтобы мой окончательный код был на доске классов, имеет вид:

public class Board
{
    private Color[,] board;

    public Board(int x, int y)
    {
        board = new Color[x, y];
    }

    public void SetColorAt(int x, int y, Color color) {
        board[x, y] = color;
    }

    public Color GetColorAt(int x, int y) {
        return board[x, y];
    }
}

Мой первый вопрос ...

как туда добраться? Могу ли я считать, что на этапе «рефакторинга» модульного теста, который я показал выше, при удалении дублирования я в конечном итоге получу этот код?

Если ответ положительный, я чувствую, что мой модульный тест тестирует что-то слишком «локализованное» для того, что на самом деле делает код. Видите ли, тест проверяет только 1 пиксель и один цвет, а сам код более сложный и насыщенный.

Может быть, решение было бы добавить больше юнит-тестов? Что бы вы посоветовали сделать?

Мой второй вопрос ...

Я знаю позже, я хочу иметь IBoard. Должен ли я просто выразить это в приведенном выше модульном тесте? Должен ли я позволить

Board board = new Board(10, 10);

как есть и все еще создавать интерфейс IBoard? Как с этим бороться?

Ответы [ 4 ]

2 голосов
/ 25 июля 2010

Вы знакомы с тестовым шаблоном Arrange-Act-Assert, верно? Вот что делает твой тест.

[TestMethod]
public void Set_The_Origin_As_Violet_And_The_Query_Confirms_It() {
    // Arrange
    Board board = new Board(10, 10);
    // Act
    board.SetColorAt(0, 0, expected);
    // Assert
    Color expected = Color.Violet;
    Assert.AreEqual(expected, board.GetColorAt(0, 0));
}

Мне нравится использовать слегка измененную форму, Arrange-AssertNot-Act-Assert. Идея состоит в том, что мы проверяем, что сам Закон - это то, что вызывает условие, для которого мы проверяем, потому что перед действием мы утверждаем, что наше условие не выполняется. Вот как это будет выглядеть:

[TestMethod]
public void Set_The_Origin_As_Violet_And_The_Query_Confirms_It() {
    // Arrange
    Board board = new Board(10, 10);
    // Assert
    Color expected = Color.Violet;
    Assert.AreNotEqual(expected, board.GetColorAt(0, 0));
    // Act
    board.SetColorAt(0, 0, expected);
    // Assert
    Assert.AreEqual(expected, board.GetColorAt(0, 0));
}

Это заставляет вас писать чуть менее простую реализацию в первый раз. Вам по-прежнему понадобятся дополнительные тесты для полной реализации GetColorAt (); для этого мне нравится чувствовать, что время для написания полной реализации - это когда меньше хлопот, чем просто фальсификация в другом особом случае (где-то около N = 3, я думаю, для этого приложения).

2 голосов
/ 25 июля 2010
public void GetColorAt(int x, int y) {
    return Color.Violet;
}

Возвращаемое здесь выражение Color.Violet может рассматриваться как дублирование данных, поэтому вы можете изменить этот бит.Самая простая вещь, которая могла бы работать, - это иметь одно значение Color, которое устанавливается при вызове SetColorAt

public class Board
{
    private Color theColor;

    public Board(int x, int y)
    {
    }

    public void SetColorAt(int x, int y, Color color) {
        theColor = color;
    }

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

Теперь вам понадобится больше тестов, чтобы показать, что вы можете установить отдельные ячейки на разные цвета..

0 голосов
/ 28 июля 2010

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

На плате слишком много деталей реализации. IOWs, цвет является свойством другого объекта. Вы действительно заботитесь о том, чтобы на плате хранились цвета, или же этот объект домена имеет свойство color Что делать, если позже вы тоже сохраните прошлую историю позиций на доске. Собираетесь ли вы использовать две доски, одну цветную, а затем историческую.

Вам действительно необходимо абстрагировать механизм хранения от фактического использования определенных типов цвета (фиолетовый, синий, зеленый и т. Д.).

Проблема с пониманием проблемной области состоит в том, что она будет мешать вашему дизайну. И он не будет таким крепким.

Для меня я бы начал с вопроса: какой объект домена является свойством цвета? Тогда мои тесты сгенерировали бы этот объект домена. Насколько я знаю Доменный объект может существовать или не существовать на доске.

Надеюсь, это поможет вам.

0 голосов
/ 25 июля 2010

Начните с описания высокого уровня того, что вы хотите реализовать.

  • Квадратная доска со строками и столбцами.
  • Каждая позиция доски должна иметь цвет.
  • Пользователь доски должен иметь возможность устанавливать цвет определенной позиции доски (пиксель).
  • Пользователь доски должен иметь возможность получить цвет определенной позиции доски.
  • и т. Д.

Затем используйте небольшие тесты для запуска реализации.Иногда легче начать с граничных условий.

When asked to get the color at a specific board position
- given a default board, and
  - the row value is less than zero
  - should throw argument exception

When asked to get the color at a specific board position
- given a default board, and
  - the row value is greater than the highest board row
  - should throw argument exception

«значение строки больше, чем самая верхняя строка доски» заставляет вас каким-либо образом установить самую высокую строку доски.Тесту не важно, устанавливаете ли вы его с помощью конструктора или установщика свойств.

When constructed with board dimensions
 - should set the highest board row

«должен установить самую высокую строку доски» заставляет вас каким-то образом получить доступ к заданному значению.Возможно получение свойства.

When asked to get the color at a specific board position
- given a default board, and
  - the column value is less than zero
  - should throw argument exception

When asked to get the color at a specific board position
- given a default board, and
  - the column value is greater than the highest board column
  - should throw argument exception

«значение столбца больше, чем столбец самой верхней доски» заставляет вас продублировать решение самой высокой строки для самого высокого столбца.

When constructed with board dimensions
 - should set the highest board column

и т. Д.

When asked to get the color at a specific board position
- given a default board, and
  - the row value is within the board space, and
  - the column is within the board space
  - should return the default color

«должен возвращать цвет по умолчанию» заставляет вас выставить где-нибудь цвет по умолчанию.Это не должно быть магическим значением, о котором знает только тест.Выставьте его как постоянное или доступное только для чтения значение на доске.

When asked to get the color at a specific board position
- given a default board, and
  - the row value is within the board space, and
  - the column is within the board space, and
  - the requested position has a known color
  - should return the color of the board at the requested position

«запрошенная позиция имеет известный цвет» вынуждает вас создать возможность устанавливать цвет позиции на доске.Вы можете сделать это сейчас, потому что у вас есть провальный тест для получения цвета в позиции.Поэтому отложите это в сторону и создайте установщик.

When asked to SET the color at a specific board position
- given a default board, and
  - the row value is less than zero
  - should throw argument exception

When asked to set the color at a specific board position
- given a default board, and
  - the row value is greater than the highest board row
  - should throw argument exception

When asked to set the color at a specific board position
- given a default board, and
  - the column value is less than zero
  - should throw argument exception

When asked to set the color at a specific board position
- given a default board, and
  - the column value is greater than the highest board column
  - should throw argument exception

When asked to set the color at a specific board position
- given a default board, and
  - the row value is within the board space, and
  - the column is within the board space
  - should set the color at the given position to the requested color

"должен установить цвет в заданной позиции равным запрошенному цвету" использует вашу реализацию get для проверки правильности установки значения.После правильной реализации все ваши тесты снова будут зелеными.

Просто продолжайте создавать дополнительные функциональные возможности по одному маленькому фрагменту за раз.

Когда вы подходите к тестам в зависимости от реализацииВ таких терминах вы можете изменить реализацию для использования IBoard или Ruby или чего-либо еще, и описания тестов не должны меняться.Вам придется изменить некоторые детали реализации теста, но заявленные цели тестов менять не нужно.

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