使用Moq验证集合中的项目的方法
我的问题并非试图简单地验证方法是否被调用。相反,我有一个方法在对象集合上工作,我想验证所有集合项目上的方法都被调用。使用Moq验证集合中的项目的方法
使用插件模型的示例,其中包含一个包含插件对象集合的插件管理器。每个插件都是PlugIn抽象基类的子类,它提供了一个抽象的Initialize方法。在我的测试中,我想确保每个插件都调用Initialize,而不管它们中的一个是否引发异常(只是大型测试套件的一部分)。
我最初的方法是创建一个mocked插件集合,然后配置被测试的类(PlugInManager)使用mocked对象。然后我通过调用应该遍历集合的PlugInManager.DoWork()来执行测试,并在每个项目上调用DoWork()。
完整的测试代码如下:
[TestMethod()]
public void MyTest()
{
// ARRANGE
var testParameter = new Something();
var mockPlugIns = new Collection<Mock<PlugIn>>()
{
new Mock<PlugIn>(),
new Mock<PlugIn>(),
new Mock<PlugIn>()
};
var plugIns = new Collection<PlugIn>();
foreach (var plugIn in mockPlugIns)
plugIns.Add(plugIn.Object);
var testManager = new PlugInManager()
{
PlugIns = plugIns
};
// ACT
testManager.DoWork(testParameter);
// ASSERT
foreach (var mockPlugIn in mockPlugIns)
mockPlugIn.Verify(plugin => plugin.DoWork(testParameter), Times.Once());
// Also tried using It.IsAny<Something>()
}
public abstract class PlugIn
{
abstract void DoWork(Something something);
}
public sealed class PlugInManager
{
public IEnumerable<PlugIn> PlugIns { get; set; }
public void DoWork(Something something)
{
foreach (var plugIn in PlugIns)
plugIn.DoWork(something);
}
}
不幸的是,验证失败的每个项目。
我已经浏览了代码,看到它实际上工作正常,并且每个项目都调用Initialize方法。那么,何时验证失败?
更新#1
我已经更新后显示整个测试方法在一个街区。我也改变了方法,需要一个参数,就像在我的真实代码中一样(现在)。
更新#2
运行测试时,收到的错误是:
Moq.MockException:
Expected invocation on the mock once, but was 0 times: plugin => plugin.DoWork(It.IsAny<Something>())
No setups configured.
No invocations performed.
如所提到的,当我通过单元测试步骤我看到,每个插件的实际被调用。然而,出于某种原因,Moq似乎没有注册或识别它。
更新#3
与测试代码的演出之后,我发现我可以使测试通过用一个简单的变化。该测试通过,如果我用下面的替换foreach循环的方法的中间:
plugIns.Add(mockPlugIns[0]);
plugIns.Add(mockPlugIns[1]);
plugIns.Add(mockPlugIns[2]);
我不明白这是怎么发挥作用,并最终喜欢做的项目数量动态的,所以在测试并不总是测试的情况下,有三个,所以使用foreach是我真正需要的。
任何想法?
这实际上并非如此,今天上午进一步测试后,我发现一切工作正常,与原始的foreach循环。我不知道发生了什么变化,但昨晚我尝试了许多不同的变体,而今天早上的代码看起来就像发布的内容一样,无论出于何种原因,测试现在都已经过去了!?!?!?!?
这适用于我在LINQPad与Moq 4。我改变的唯一办法是在Times.Once()
上加上括号。
void Main()
{
var MockPlugIns = new Collection<Mock<PlugIn>>()
{
new Mock<PlugIn>(),
new Mock<PlugIn>(),
new Mock<PlugIn>()
};
var plugIns = new Collection<PlugIn>();
foreach (var mockPlugIn in MockPlugIns)
plugIns.Add(mockPlugIn.Object);
var testManager = new PlugInManager()
{
PlugIns = plugIns
};
testManager.Initialize();
foreach (var mockPlugIn in MockPlugIns)
mockPlugIn.Verify(plugin => plugin.Initialize(), Times.Once());
}
public abstract class PlugIn
{
public abstract void Initialize();
}
public class PlugInManager
{
public void Initialize()
{
foreach (var plugIn in PlugIns)
{
plugIn.Initialize();
}
}
public Collection<PlugIn> PlugIns { get; set; }
}
UPDATE
我跑更新的测试代码,它通过给下面的实现:
public class PlugInManager
{
public void DoWork(Something s)
{
foreach (var plugIn in PlugIns)
{
plugIn.DoWork(s);
}
}
public Collection<PlugIn> PlugIns { get; set; }
}
它通过带或不带你提到的It.IsAny变化。最初的想法是你可能没有将相同的Something实例传递给插件,但It.IsAny会解决这个问题。
简而言之,看起来你在测试中做的一切都是正确的。也许这个问题是在实际实施中。
请在测试失败时发布您的PlugInManager.DoWork和确切错误消息。另外,你使用什么版本的Moq?
UPDATE
我剪切和粘贴代码,并尝试过。我不得不作出一个更改:abstract void DoWork
上abstract class PlugIn
需要是public
。完成更改后,编译并通过测试。如果我将测试的“ACT”部分注释掉,它会失败并显示您看到的错误消息(正如我所预期的那样)。
在您的项目或环境中有所不同。我使用Moq 4.0在Windows 64下运行.NET 4(不是Mono)。你发布的所有内容都是正确的。我会建议您确认您正在运行最新的Moq二进制文件,检查您的项目参考,并尝试一些非常简单的验证测试以确保Moq正在运行。
你的实现基本上就是这样。我已经使用代码更新了帖子以及我收到的错误。 – SonOfPirate 2012-01-30 15:23:45
是的,'public'是一个剪切粘贴错误(我在重新格式化时删除了太多的内容)我使用Moq 4在Windows 7 32位下运行VS 2010,.NET 4。测试在解决方案中,大多数使用Moq,这是唯一的测试给我的问题,看我的更新,我最近的调查结果 – SonOfPirate 2012-01-31 02:57:57
该行应该是'plugIns.Add(mockPlugIns [0] .Object);'。其他在这一点上我不知道发生了什么 – TrueWill 2012-01-31 03:20:35
您能提供完整的测试或简化版吗?因为你的方法如果好,它应该工作。我还用你的代码创建了一个repro,它对我来说是绿色的。 – nemesv 2012-01-29 08:42:14