C# 用于单元测试的接口/虚拟域模型

C# 用于单元测试的接口/虚拟域模型,c#,unit-testing,interface,moq,C#,Unit Testing,Interface,Moq,我正在开发一个.Net核心Web应用程序,该应用程序正在使用NUnit和Moq进行单元测试。出于提问目的,下面的示例非常简化 我有一个“purchaseOrderService”类,它从存储库获取采购订单并调用 purchaseOrder.Cancel(); 在保存到存储库之前。PurchaseOrder和StockItem类如下所示 public class PurchaseOrder { public int Id { get; set; } public List<

我正在开发一个.Net核心Web应用程序,该应用程序正在使用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;
    }
}
公共类采购订单
{
公共int Id{get;set;}
公共列表StockItems{get;set;}
public PurchaseOrderStatus状态{get;private set;}
公开作废取消()
{
状态=PurchaseOrderStatus。已取消;
foreach(StockItems中的var stockItem)
stockItem.Cancel();
}
}
公共类库存项目
{
公共int Id{get;set;}
public StockItemStatus状态{get;private set;}
公开作废取消()
{
状态=StockItemStatus。已取消;
}
}
在purchaseOrder.Cancel方法的单元测试中,我想模拟stockItem,以便验证是否对采购订单中的每个stockItem调用了Cancel Cancel方法一次

我通常会通过以下方式实现这一点

Mock<StockItem> mockSI = new Mock<StockItem>();
mockSI.Setup(x => x.Cancel());
mockSI.Setup( x=> x.Cancel(), Times.Once);
Mock mockSI=new Mock();
mockSI.Setup(x=>x.Cancel());
mockSI.Setup(x=>x.Cancel(),次.Once);
但是,域模型不是作为接口公开的,并且Cancel方法不是虚拟的,因此不能出于模拟的目的重写Cancel方法

这让我有3个选择

  • 使cancel方法虚拟化——这似乎是一个糟糕的想法,并且会在不需要的时候将其覆盖

  • 为需要模拟的域模型创建一个接口——这似乎太过分了,因为我将只为测试而创建接口,而不打算让任何其他类继承接口和我完全控制的类。StackOverflow上的多篇文章都说,没有充分理由的域模型接口是不好的做法

  • 比如说,,

  • 将其视为一个集成测试,并测试两个类的协同工作

  • 目前,我倾向于让StockItem类实现一个接口。您有什么建议?

    此处无需实现接口

    只需要重新思考如何验证预期行为

    取消采购订单后检查项目的状态,这应足以表明/验证调用了
    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);
    }
    
    [测试]
    当采购订单取消时,公共作废库存项目应取消(){
    //安排
    var item=newstockitem();
    var purchaseOrder=新的purchaseOrder(){
    StockItems=新列表{
    项目
    }
    };
    //表演
    purchaseOrder.Cancel();
    //断言
    item.Status.Should().Be(StockItemStatus.Cancelled);
    }
    
    此处无需实现接口。只需要重新思考如何验证预期行为。在调用cancel后检查项目的状态,这应该足以表明调用了cancel。这就是我对选项3的暗示。但我发现,我在StockItem中测试了cancel方法,在PurchaseOrder中进行了几乎重复的测试。感觉有点不对劲,就像我在做比单元测试更大的事情一样,选择选项3,因为这是更符合逻辑的选择。