C# 如何使用Moq测试没有返回值的方法?

C# 如何使用Moq测试没有返回值的方法?,c#,.net,unit-testing,mocking,moq,C#,.net,Unit Testing,Mocking,Moq,这是我的第一个问题,所以请客气点!:) 我试图做的是为一个manager类编写一些测试,该类在构建过程中会向列表中添加单个item类的许多新实例。当在这个管理器类中调用UpdateAllItems时,目的是迭代列表并对每个项目调用增量 manager类是我的代码,但single item类不是,因此我无法修改它 我使用NUnit作为测试框架,并开始使用Moq。因为manager类使用了singleitem类,所以我认为我需要使用Moq,所以我只测试manager,而不是singleitem 如何

这是我的第一个问题,所以请客气点!:)

我试图做的是为一个manager类编写一些测试,该类在构建过程中会向列表中添加单个item类的许多新实例。当在这个管理器类中调用UpdateAllItems时,目的是迭代列表并对每个项目调用增量

manager类是我的代码,但single item类不是,因此我无法修改它

我使用NUnit作为测试框架,并开始使用Moq。因为manager类使用了singleitem类,所以我认为我需要使用Moq,所以我只测试manager,而不是singleitem

如何为UpdateAllItems方法编写测试?(从技术上讲,我应该先编写测试)

下面是一些示例代码,它给出了我正在使用的东西的一般概念

public class SingleItem_CodeCantBeModified
{
    public int CurrentValue { get; private set; }

    public SingleItem_CodeCantBeModified(int startValue)
    {
        CurrentValue = startValue;
    }

    public void Increment()
    {
        CurrentValue++;
    }
}

public class SingleItemManager
{
    List<SingleItem_CodeCantBeModified> items = new List<SingleItem_CodeCantBeModified>();

    public SingleItemManager()
    {
        items.Add(new SingleItem_CodeCantBeModified(100));
        items.Add(new SingleItem_CodeCantBeModified(200));
    }

    public void UpdateAllItems()
    {
        items.ForEach(item => item.Increment());
    }
}
public类SingleItem\u CodeCantBeModified
{
public int CurrentValue{get;private set;}
公共单项_CodeCantBeModified(int起始值)
{
CurrentValue=起始值;
}
公共空间增量()
{
CurrentValue++;
}
}
公共类SingleItemManager
{
列表项=新列表();
公共单项目管理器()
{
添加(新的单一项_CodeCantBeModified(100));
添加(新的单一项_CodeCantBeModified(200));
}
public void UpdateAllItems()
{
items.ForEach(item=>item.Increment());
}
}

提前感谢所有的帮助

您的经理过于依赖项目(在
列表中)。您能将列表填充提取到单独的类中以模拟它吗?e、 g:

public SingleItemManager()
{
    items.Add(ItemRepository.Get(100));
    items.Add(ItemRepository.Get(200));
}
测试(部分代码省略):

inti=0;
var itemMock=new Mock();
Setup(i=>i.Increment()).Callback(()=>i++);
var repositoryMock=new Moc();
Setup(r=>r.Get(It.IsAny()).Returns(itemMock.Object);
var manager=新的SingleItemManager();
manager.UpdateAllItems();
断言.AreEqual(i,1);

不要硬编码额外的具体项目,而是让
SingleItem\u CodeCantBeModified
实现一个接口(或将其嵌入实现接口的包装器中),然后传入一个(新的)工厂,该工厂将创建这些项目

在您的测试中,您将创建一个工厂的模拟以传递给Manager类,然后您可以监视在该模拟对象上调用了哪些方法


虽然这更多的是测试系统的内部,而不是副产品。管理器实现的是什么接口?如果它没有在外部证明自己,您测试的结果是什么?

像往常一样,您可以添加另一个间接级别

  • 围绕
    SingleItem\u CodeCantBeModified
  • 使此包装器继承
    IItem
    接口
  • 使
    SingleItemManager
    依赖
    IItem
    而不是
    SingleItem\u CodeCantBeModified

  • 如果
    Increment
    是一个虚拟方法(我知道它不在示例代码中,只是以防万一),请使用。

    简单的答案是,你不能。
    updatealItems
    调用的方法(
    Increment()
    )是非虚拟的,因此你无法模拟它

    在我看来,你的选择是:

      完全不要测试<代码> UpDATEALLATION< /COD>。它的实现是微不足道的,所以这是一个需要考虑的选项(虽然不理想)。
    • 在测试中创建真正的
      SingleItem\u CodeCantBeModified
      实例。纯粹主义者会说,此时您不再有单元测试,但它仍然可能是一个有用的测试
    • 添加一个
      ISingleItem
      接口和一个
      ISingleItemAdapter:ISingleItem
      类,该类保留对
      SingleItem\u CodeCantBeModified
      的引用并转发调用。然后,您可以编写
      SingleItemManager
      ISingleItem
      进行操作,并且可以自由地传递模拟
      ISingleItem测试中的。(取决于系统的设置方式,您甚至可以从
      SingleItem\u CodeCantBeModified
      ,在后代上实现接口,并使用这些对象而不是编写适配器。)

    最后一个选项为您提供了最多的选项,但要付出一定的复杂性。请选择最适合您尝试完成的任务的选项。

    SingleItem\uCodeCantBeModified@riezebosch:如果push出现问题,您可以创建一个代理对象来实现interfacet,并且只通过方法调用这与获取类来实现接口一样,但具有相同的效果。这个类有何用处?它创建计数器的私有列表,并在每次调用方法时更新每个项。对象的行为必须有一些更改。例如,如果UpdateAllItems不起作用-观察到的更改是什么?
    int i = 0;
    
    var itemMock = new Mock<Item>();
    itemMock.Setup(i => i.Increment()).Callback(() => i++);
    
    var repositoryMock = new Moc<ItemRepository>();
    repositoryMock.Setup(r => r.Get(It.IsAny<int>()).Returns(itemMock.Object);
    
    var manager = new SingleItemManager();
    manager.UpdateAllItems();
    
    Assert.AreEqual(i, 1);