如何仅在依赖于方法2时测试方法1

时间:2015-12-21 19:01:20

标签: c# unit-testing moq

鉴于我有一个带有(示例)实现的WCF服务:

public interface IFoo
{
    void Bar();
    void Baz();
}

public interface ISomeDependency
{
    void DoStuff();
}

public class Foo : IFoo
{

    ISomeDependency _someDependency;

    public Foo(ISomeDependency someDependency)
    {
        this._someDependency = someDependency;
    }

    public void Bar()
    {
        // Some stuff

        _someDependency.DoStuff();

        if (1 == 1) // some condition that won't always be true
            this.Baz();
    }

    public void Baz()
    {
        // some stuff

        _someDependency.DoStuff();
    }
}

如何在不关注Foo.Bar的结果的情况下进行单元测试Foo.Baz的实施?具体来说,我想知道Foo.Baz();曾经(或未被)调用取决于我如何模仿对Foo.Bar的调用,但不一定要Foo.Baz逻辑“触发”。

我原本打算做这样的事情:

public class Foo : IFoo
{
    // ... same as previous

    public virtual void Baz()
    {
        // some stuff

        _someDependency.DoStuff();
    }
}

然后在我的单元测试项目中:

public class TestFoo : Foo
{
    public bool IsBazFired { get; private set; }

    public TestFoo(ISomeDependency someDependency)
        : base (someDependency)
    {
        IsBazFired = false;
    }

    public override void Baz()
    {
        IsBazFired = true;
    }
}

通过这种方式,我可以看到Foo.Baz在我的测试中被解雇了(虽然我必须使用TestFoo而不是Foo进行测试。我还有其他方法可以执行此操作吗?现在似乎工作不够,但如果试图在整个地方实现这一点,那么代码可能/将会被类的测试实现所困扰。

我不一定喜欢将我的函数标记为virtual,因此我可以将实现用于测试...所以我希望有另一种方法。

我目前刚刚开始使用模拟框架Moq,如果这对如何实现我想要的结果有所帮助。

1 个答案:

答案 0 :(得分:0)

以下是我使用Moq完成测试的方法。

首先,将需要“删除”的方法标记为虚拟(希望这不被视为不良做法):

public virtual void Baz()
{
    // some stuff

    _someDependency.DoStuff();
}

在测试中,您可以设置类的模拟对象,而不是接口。到目前为止,我只是在嘲笑界面:

[TestClass]
public class FooTests
{

    Mock<Foo> _mockFoo;
    Mock<ISomeDependency> _mockSomeDependency;

    [TestInitialize]
    public void Setup()
    {
        _mockSomeDependency = new Mock<ISomeDependency>();
        _mockFoo = new Mock<Foo>(_mockSomeDependency.Object);
    }

    [TestMethod]
    public void Testing_BazIsNotCalled()
    {
        _mockFoo.CallBase = true;
        _mockFoo.Setup(s => s.Baz());
        _mockFoo.Object.Bar();

        _mockFoo.Verify(v => v.Baz(), Times.Never);
    }

    [TestMethod]
    public void Testing_BazIsCalled()
    {
        _mockFoo.CallBase = true;
        _mockFoo.Setup(s => s.Baz());
        _mockFoo.Object.Bar();

        _mockFoo.Verify(v => v.Baz(), Times.Once);
    }
}

在我的模拟上使用CallBase = true表示未明确模拟的任何内容都将使用类实现。

在我的_mockFoo.Setup(s => s.Baz());中,我正在覆盖Baz()的“基本”功能并提供新的实现(在本例中为存根)。然后,我实际上调用了基础Bar(),并使用_mockFoo.Verify(...);进行测试,无论Baz()调用是否继续进行。这个验证检查当然是基于if (1==1),在这种情况下总是如此,但我只想在那里得到一些“假逻辑”,所以我会有一个块来检查。

请注意,如果您忘记将您的方法标记为虚拟存根,那么您的代码编译正常,但在运行时遇到异常,类似于:

Result StackTrace:    
...
at Moq.Mock.ThrowIfCantOverride(Expression setup, MethodInfo method)
   at Moq.Mock.<>c__DisplayClass1c`2.<Setup>b__1b()
   at Moq.PexProtector.Invoke[T](Func`1 function)
   at Moq.Mock.Setup[T,TResult](Mock`1 mock, Expression`1 expression, Condition condition)

at Moq.Mock`1.Setup[TResult](Expression`1 expression)
    at FooTests.Testing_BazIsNotCalled() in UnitTests.cs:line 23
Result Message: Initialization method Testing_BazIsNotCalled threw     exception. System.NotSupportedException: System.NotSupportedException: Invalid     setup on a non-virtual (overridable in VB) member: s => s.Baz()).

我不知道我是否正在使用上述(存根/模拟)的正确术语,但它仍在使用。