Модульное тестирование подтверждает дублирование - PullRequest
4 голосов
/ 08 февраля 2011

Я изучаю TDD и экспериментирую с ним в моем текущем проекте. Я заметил, что мне приходится дублировать многие утверждения в моих тестах. Вот ситуация: У меня есть класс Order с двумя конструкторами первый по умолчанию и второй имеет три параметра

Order(int customerId, int typeId, decimal amount)

В классе OrderTests я проверяю, что задания работают хорошо

Assert.IsTrue(o.CustomerId == 5 && o.TypeId == 3 && amount == 500)

У меня есть класс обслуживания заказа со следующим методом создания заказа, поскольку создание заказа является сложным процессом.

Order CreateOrder(int cusotmerId, int typeId, int amount, moreParams...)

Класс OrderServiceTests имеет тест для этого метода, и мне нужно использовать тот же assert, чтобы проверить, правильно ли был создан Order в сервисе CreateOrder.

Assert.IsTrue(o.CustomerId == 5 && o.TypeId == 3 && amount == 500)
  1. Можно ли иметь такие дубликаты в тестах?
  2. Имеет ли смысл извлекать методы с теми же утверждениями в тестах, что иногда число или дублированные утверждения могут быть более одного? Или такой метод извлечения делает тесты нечитаемыми?

Ответы [ 3 ]

4 голосов
/ 09 февраля 2011

Если у вас есть несколько способов создания объекта, вы можете проверить состояние объекта для каждого из методов создания (то есть параметризованного конструктора, а также метода фабрики). Таким образом, имеет смысл дублировать утверждения.

Во время рефакторинга после прохождения теста (всегда помните мантру: красный-зеленый-рефактор), если вы обнаружите дублирование не только в рабочем коде, но и в тестах, то вы должны удалить его, например с использованием метода рефакторинга Extract Method.

[TestMethod]
public void if_parametrized_ctor_is_called_then_state_should_be_accordingly {
  var order = new Order(customerId, ...);
  ObjectPropertiesShouldBeSetTo(order, customerId, ...);
}

[TestMethod]
public void if_factory_method_is_called_then_state_should_be_accordingly {
  var order = myFactory.CreateOrder(customerId, ...);
  ObjectPropertiesShouldBeSetTo(order, customerId, ...);
}

// Extracted to remove code duplication
public void ObjectPropertiesShouldBeSetTo(Order order, int customerId, ...) {
  Assert.AreEqual(customerId, order.CustomerId);
  Assert.AreEqual(...);
}

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

3 голосов
/ 08 февраля 2011

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

Еще одна вещь, касающаяся вашего примера Assert. Объединение нескольких проверок в одном Assert усложняет определение из-за ошибки, какое свойство было ошибочным. Если вы разделите их на отдельные подтверждения и предоставите сообщения для каждого, это значительно упростит отслеживание ошибки (особенно если вы используете сервер непрерывной интеграции, такой как CruiseControl.Net.). Изменение вашего примера:

Assert.IsTrue(o.CustomerID == 5, "CustomerID doesn't match expected");
Assert.IsTrue(o.TypeId == 3, "TypeID doesn't match expected");
Assert.IsTrue(amount == 500, "Amount doesn't match expected");
1 голос
/ 08 февраля 2011

Да, модульные тесты часто вызывают дублирование кода.Но это дублирование имеет значение.Идея состоит в том, что если вы пишете одну и ту же вещь двумя способами, вы вряд ли совершите одну и ту же ошибку оба раза.А это значит, что вы будете ловить много глупых ошибок.(Предположительно половина из них содержится в коде вашего модульного теста.) Автоматическая генерация модульных тестов из вашего кода приводит ко всему дублированию кода и уменьшению преимуществ.

...