Подтвердить что ты не робот

Уровень обслуживания приложений: модульные тесты, интеграционные тесты или оба?

У меня есть куча методов на моем уровне сервисов приложений, которые делают такие вещи:

public void Execute(PlaceOrderOnHoldCommand command)
{
    var order = _repository.Load(command.OrderId);
    order.PlaceOnHold();
    _repository.Save(order);
}

И в настоящее время у меня есть куча модульных тестов:

[Test]
public void PlaceOrderOnHold_LoadsOrderFromRepository()
{
    var repository = new Mock<IOrderRepository>();
    const int orderId = 1;
    var order = new Mock<IOrder>();
    repository.Setup(r => r.Load(orderId)).Returns(order.Object);

    var command = new PlaceOrderOnHoldCommand(orderId);
    var service = new OrderService(repository.Object);
    service.Execute(command);

    repository.Verify(r => r.Load(It.Is<int>(x => x == orderId)), Times.Exactly(1));
}

[Test]
public void PlaceOrderOnHold_CallsPlaceOnHold()
{
            /* blah blah */
}

[Test]
public void PlaceOrderOnHold_SavesOrderToRepository()
{
            /* blah blah */
}

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

Если уровень сервиса приложения должен быть протестирован на этот уровень детализации или достаточны тесты интеграции?

4b9b3361

Ответ 1

именно: это спорно! Это действительно хорошо, что вы взвешиваете расходы/усилие написания и поддержания вашего теста против ценности, которую оно принесет вам, - и это именно то внимание, которое вы должны сделать для каждого теста, который вы пишете. Часто я вижу тесты, написанные для тестирования и тем самым добавляя балласт к базе кода.

Как правило, я обычно принимаю, что мне нужен полный интеграционный тест для каждого важного успешного сценария/варианта использования. Другие тесты, которые я напишу, предназначены для частей кода, которые могут сломаться с будущими изменениями или сломаться в прошлом. И это определенно не весь код. Это означает, что ваше мнение и понимание системы и требований вступают в игру.

Предполагая, что у вас есть (интеграционный) тест для service.Execute(placeOrderOnHoldCommand), я не уверен, добавляет ли он значение для проверки, если служба загружает заказ из репозитория ровно один раз. Но это может быть! Например, когда у вашего сервиса ранее была неприятная ошибка, которая попадала бы в репозиторий десять раз за один заказ, вызывая проблемы с производительностью (просто создавая ее). В этом случае я переименовал бы тест на PlaceOrderOnHold_LoadsOrderFromRepositoryExactlyOnce().

Поэтому для каждого теста вы должны сами решить... надеюсь, что это поможет.

Примечания:

  • Те тесты, которые вы показываете, могут быть абсолютно корректными и выглядеть хорошо написанными.

  • Ваши методы тестовой последовательности, похоже, вдохновлены тем, как метод Execute(...) в настоящее время реализован. Когда вы структурируете свой тест таким образом, возможно, вы привязываетесь к конкретной реализации. Таким образом, тесты могут существенно затруднить изменение - убедитесь, что вы только проверяете важное внешнее поведение вашего класса.

Ответ 2

Я бы написал unit test, несмотря на то, что также был интеграционный тест. Тем не менее, я, скорее всего, сделаю тест намного проще, устранив насмешливую структуру, нарисуя мой собственный простой макет, а затем объединяя все эти тесты, чтобы проверить, что порядок в макетном репозитории приостановлен.

[Test]
public void PlaceOrderOnHold_LoadsOrderFromRepository()
{
    const int orderId = 1;
    var repository = new MyMockRepository();
    repository.save(new MyMockOrder(orderId));      
    var command = new PlaceOrderOnHoldCommand(orderId);
    var service = new OrderService(repository);
    service.Execute(command);
    Assert.IsTrue(repository.getOrder(orderId).isOnHold());
}

Нет необходимости проверять, чтобы вызывать загрузку и/или сохранение. Вместо этого я просто удостоверился, что единственный способ вернуть MyMockRepository обновленный порядок - это вызвать загрузку и сохранение.

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

Ответ 3

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

Я думаю, что важно иметь и то и другое, и да, можно протестировать все это только с помощью интеграционных тестов, но это делает ваши тесты длительными и сложными для отладки. В среднем я думаю, что у меня 10 тестов на интеграцию.

Я не пытаюсь тестировать методы с помощью однострочных, если в этой строке не происходит что-то вроде логики.

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