Я работаю над базовым веб-приложением .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 варианта
Сделать метод отмены Виртуальный - Это кажется ужасной идеей и подвергает ее переопределению в нежелательное время.
Создание интерфейса для модели предметной области, которая должна быть смоделирована. Это кажется чрезмерным, так как я бы делал интерфейс только для тестирования, не планируя, чтобы какой-либо другой класс наследовал интерфейс, и для классов, которые я полностью контролирую. В нескольких публикациях в StackOverflow говорится, что интерфейсы для доменных моделей без уважительной причины являются плохой практикой.
например,
Интерфейсы для богатых доменных моделей
- Рассматривайте это как интеграционный тест и тестируйте 2 класса, работающих вместе
В данный момент я склоняюсь к тому, чтобы заставить класс StockItem реализовать интерфейс. Что бы вы порекомендовали?