使用Moq验证对集合中项目的方法调用

时间:2012-01-29 04:10:57

标签: moq

我的问题不是简单地验证方法是否被调用。相反,我有一个方法可以处理对象集合,我想验证是否正在调用所有集合项的方法。

使用插件模型的示例,其中我有一个插件管理器,其中包含一组插件对象。每个插件都是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循环一切正常。我不知道发生了什么变化但是昨晚我尝试了很多不同的变化,而今天早上的代码看起来就像发布的那样,无论出于何种原因,测试现在正在通过!?!?!?!?

1 个答案:

答案 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正在运行。