我的问题不是简单地验证方法是否被调用。相反,我有一个方法可以处理对象集合,我想验证是否正在调用所有集合项的方法。
使用插件模型的示例,其中我有一个插件管理器,其中包含一组插件对象。每个插件都是PlugIn抽象基类的子类,它暴露了一个抽象的Initialize方法。在我的测试中,我想确保在每个插件上调用Initialize,无论其中一个插件是否抛出异常(只是更大的测试套件的一部分)。
我最初的方法是创建一个模拟插件集合,然后配置测试中的类(PlugInManager)以使用模拟对象。然后我通过调用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循环一切正常。我不知道发生了什么变化但是昨晚我尝试了很多不同的变化,而今天早上的代码看起来就像发布的那样,无论出于何种原因,测试现在正在通过!?!?!?!?
答案 0 :(得分:2)
这对我在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; }
}
<强>更新强>
我运行了更新的测试代码,并通过了以下实现:
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?
<强>更新强>
我剪切并粘贴了您的代码并尝试了它。我必须进行一项更改:abstract void DoWork
上的abstract class PlugIn
需要public
。进行更改后,它会编译并且测试通过。如果我注释掉你的测试的“ACT”部分,它会因你看到的错误消息而失败(正如我所料)。
您的项目或环境中有些不同。我在Windows 64下用Moq 4.0运行.NET 4(不是Mono)。你发布的所有内容都是正确的。我建议您确认您正在运行最新的Moq二进制文件,检查项目引用,并尝试一些非常简单的验证测试以确保Moq正在运行。