【问题标题】:How to test method1 only when it depends on method 2仅当依赖于方法2时如何测试方法1
【发布时间】:2015-12-21 19:01:20
【问题描述】:

假设我有一个带有(示例)实现的 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.Baz 的结果的情况下对Foo.Bars 实施进行单元测试?具体来说,我想知道 Foo.Baz();是否被调用取决于我如何模拟对Foo.Bar 的调用,但不一定希望Foo.Bazs 逻辑“触发”。

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

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,如果这对如何实现我想要的结果有影响的话。

【问题讨论】:

  • 您可以使用模拟框架,例如,FakeItEasyNSubstitute
  • 就像 Sergii 所说,没有模拟框架,您必须像以前那样手动完成。
  • 我目前正在使用(或尝试学习)Moq,但还没有使用混凝土,只有接口模拟
  • 如果您不想(或不能)使用virtual,有一个模拟框架Microsoft Fakes 可以帮助您使用垫片测试几乎所有内容。 A shim modifies the compiled code of your application at run time so that instead of making a specified method call, it runs the shim code that your test provides.

标签: c# unit-testing moq


【解决方案1】:

以下是我使用 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 =&gt; 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()).

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 2015-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多