2011-09-02 73 views
5

这是我的第一个问题,请善待! :)如何使用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()); 
    } 
} 

感谢所有帮助!

+0

这个类是如何有用的?它创建一个专用的计数器列表,并在每次调用某个方法时更新每个项目。对象的行为必须有一些改变。如果UpdateAllItems不起作用 - 将观察到的变化是什么? – Gishu

回答

5

简单的答案是,你不能。 UpdateAllItems调用的方法(Increment())是非虚拟的,因此您将无法嘲笑它。

您的选择,在我看来,主要有:

  • 不要测试UpdateAllItems可言。它的实现是微不足道的,所以这是一个可以考虑的选项(虽然不是很理想)。
  • 在您的测试中创建实际的SingleItem_CodeCantBeModified实例。纯粹主义者会说在这一点上你不再有单元测试,但它仍然是一个有用的测试。
  • 添加一个ISingleItem接口和一个SingleItemAdapter : ISingleItem类,该类保存对SingleItem_CodeCantBeModified的引用并转发呼叫。然后,您可以编写SingleItemManager以在ISingleItem上运行,并且您可以在测试中自由传递模拟ISingleItem。 (根据您的系统是如何设置的,你甚至可以从SingleItem_CodeCantBeModified下降,实现你的后裔接口,并使用这些对象,而不是写一个适配器。)

这最后一个选项为您提供了大多数选择,但是以一些复杂性为代价。选择最适合您尝试完成的选项。

1

你的经理太依赖于物品(在List<Item>)。你可以提取列表人口到单独的类,以便能够嘲笑它吗?例如:

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

而不是硬编码的额外的具体项目,有SingleItem_CodeCantBeModified实现一个接口(或者它实现了接口的包装,它嵌入),然后通过一个(新)工厂,这将创建这些项目。

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

虽然这将更多地测试系统的内部,而不是副产品。管理器实施什么界面?如果它没有从外部证明自己,你测试的结果是什么?

+0

SingleItem_ ** CodeCantBeModified ** – riezebosch

+0

@riezebosch:如果push来推,您可以创建一个代理对象,实现一个interfact,并通过方法调用。它与gettting类实现接口的效果不尽相同,但效果相同。 – Chris

0

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

  1. 创造约SingleItem_CodeCantBeModified
  2. 一个包装类,使此包装继承IItem接口
  3. SingleItemManager取决于IItem而不是SingleItem_CodeCantBeModified

OR

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

相关问题