Интерфейсы / Виртуальные для доменных моделей для модульного тестирования - PullRequest
0 голосов
/ 30 октября 2018

Я работаю над базовым веб-приложением .Net, которое проходит модульное тестирование с помощью NUnit и Moq. Приведенный ниже пример очень упрощен для вопросов.

У меня есть класс «purchaseOrderService», который выбирает заказ на покупку из репозитория и вызывает

purchaseOrder.Cancel();

перед сохранением в хранилище. Ниже представлены классы PurchaseOrder и StockItem,

public class PurchaseOrder
{
    public int Id { get; set; }

    public List<StockItem> StockItems { get; set; }

    public PurchaseOrderStatus Status { get; private set; }

    public void Cancel()
    {
        Status = PurchaseOrderStatus.Cancelled;
        foreach(var stockItem in StockItems)
            stockItem.Cancel();
    }
}

public class StockItem
{
    public int Id { get; set; }

    public StockItemStatus Status { get; private set; }

    public void Cancel()
    {
        Status = StockItemStatus.Cancelled;
    }
}

В модульном тесте для моего метода purchaseOrder.Cancel я хотел смоделировать stockItem, чтобы я мог убедиться, что метод отмены отмены вызывался один раз для каждого stockItem внутри заказа на покупку.

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

Mock<StockItem> mockSI = new Mock<StockItem>();
mockSI.Setup(x => x.Cancel());
mockSI.Setup( x=> x.Cancel(), Times.Once);

Однако модель предметной области не отображается как интерфейс, а метод Cancel не является виртуальным, поэтому метод Cancel нельзя переопределить в целях Mock.

Это оставляет мне 3 варианта

  1. Сделать метод отмены Виртуальный - Это кажется ужасной идеей и подвергает ее переопределению в нежелательное время.

  2. Создание интерфейса для модели предметной области, которая должна быть смоделирована. Это кажется чрезмерным, так как я бы делал интерфейс только для тестирования, не планируя, чтобы какой-либо другой класс наследовал интерфейс, и для классов, которые я полностью контролирую. В нескольких публикациях в StackOverflow говорится, что интерфейсы для доменных моделей без уважительной причины являются плохой практикой.

например, Интерфейсы для богатых доменных моделей

  1. Рассматривайте это как интеграционный тест и тестируйте 2 класса, работающих вместе

В данный момент я склоняюсь к тому, чтобы заставить класс StockItem реализовать интерфейс. Что бы вы порекомендовали?

1 Ответ

0 голосов
/ 30 октября 2018

Нет необходимости реализовывать интерфейсы здесь.

Просто нужно переосмыслить, как проверить ожидаемое поведение.

Проверьте состояние товаров после отмены заказа на покупку, и этого должно быть достаточно, чтобы указать / подтвердить, что StockItem.Cancel был вызван.

[Test]
public void StockItem_Should_Cancel_When_PurchaseOrder_Cancelled() {
    //Arrange
    var item = new StockItem();
    var purchaseOrder = new PurchaseOrder() {
        StockItems = new List<StockItem> { 
            item
        }
    };

    //Act
    purchaseOrder.Cancel();

    //Assert
    item.Status.Should().Be(StockItemStatus.Cancelled);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...