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

时间:2011-09-02 12:39:42

标签: c# .net unit-testing mocking moq

这是我的第一个问题所以请善待! :)

我要做的是为管理器类编写一些测试,在构造期间将单个项类的许多新实例添加到列表中。在此管理器类中调用UpdateAllItems时,目的是迭代列表并对每个项目调用Increment。

经理类是我的代码,但单项类不是,所以我无法修改它。

我使用NUnit作为测试框架,并开始使用Moq。因为经理类使用单项类我认为我需要使用Moq所以我只测试经理,而不是单项。

如何为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());
    }
}

提前感谢所有帮助!

4 个答案:

答案 0 :(得分:5)

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

我认为你的选择是:

  • 根本不要测试UpdateAllItems。它的实现是微不足道的,所以这是一个考虑的选择(虽然不是理想的)。
  • 在测试中创建真实SingleItem_CodeCantBeModified个实例。纯粹主义者会说你现在不再有单位测试了,但它仍然是一个有用的测试。
  • 添加ISingleItem接口和SingleItemAdapter : ISingleItem类,该类保留对SingleItem_CodeCantBeModified的引用并转发呼叫。然后,您可以编写SingleItemManager来操作ISingleItem,并且您可以在测试中自由传递模拟ISingleItem。 (根据系统的设置方式,您甚至可以从SingleItem_CodeCantBeModified下载,在后代上实现接口,并使用这些对象而不是编写适配器。)

最后一个选项为您提供了最多选项,但代价是复杂性。选择最适合您要完成的选项。

答案 1 :(得分:1)

您的经理过于依赖项目(List<Item>)。你能将列表填充提取到单独的类中以便能够模拟它吗? e.g:

public SingleItemManager()
{
    items.Add(ItemRepository.Get(100));
    items.Add(ItemRepository.Get(200));
}

测试(省略一些代码):

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);

答案 2 :(得分:0)

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

  1. 围绕SingleItem_CodeCantBeModified
  2. 创建一个包装类
  3. 使此包装器继承IItem interface
  4. SingleItemManager取决于IItem而不是SingleItem_CodeCantBeModified
  5. OR

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

答案 3 :(得分:-1)

不要对其他具体项进行硬编码,而是让SingleItem_CodeCantBeModified实现一个接口(或将其嵌入实现接口的包装器中),然后传入将创建这些项的(新)工厂。

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

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