C# 仅当方法1依赖于方法2时,如何测试方法1

C# 仅当方法1依赖于方法2时,如何测试方法1,c#,unit-testing,moq,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 someDependenc

假设我有一个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
s实现而不关心
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
,这样我就可以删除一个实现进行测试……所以我希望有另一种方法


我目前刚开始使用mock框架
Moq
,如果这对如何实现我想要的结果有影响的话。

下面是我如何使用
Moq
完成测试的

首先,将需要“剔除”的方法标记为虚拟方法(希望这不被认为是坏做法):

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

[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);
    }
}

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

例如,你可以使用模拟框架。正如Sergii所说,如果没有模拟框架,你必须像你那样手动操作。我目前正在使用(或尝试学习)
Moq
,但尚未使用混凝土,如果您不想(或不能)只使用接口模拟要使用
virtual
,有一个模拟框架可以帮助您使用填隙片测试几乎所有内容。
填隙片在运行时修改应用程序的编译代码,以便它运行测试提供的填隙片代码,而不是进行指定的方法调用。
[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);
    }
}
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()).