在单元测试中跳过内部方法调用

Skip inner method calls in unit testing

比方说,我有方法 A、B、C 和 D。

public bool A (int foo)
{
    bool result = false;
    if (foo > 0)
        result = B();
    else result = C();
    D(foo);
    return result;
}

我想为A写一个单元测试,调用B或C,但想跳过D调用(因为它是一个使用外部服务的方法)。是否可以使用某些属性跳过 D 调用?或者模拟 D,用一些虚假服务代替它?

这凸显了设计代码使其可进行单元测试的重要性。依赖注入在这方面非常有用。然后,您可以在单元测试时模拟依赖项。例如,您可能有一个可以通过接口 ICommunications 访问的通信层。然后,您的 class 将在其构造函数中引用一个 ICommunications 对象:

public class TestableClass
{
    private ICommunications _comms;
    public TestableClass(ICommunications comms)
    {
        _comms = comms;
    }

    public bool FunctionToTest()
    {
        //do something testable

        _comms.SomeFunction();//mocked object in unit tests

        //do something else testable
    }
}

然后只需创建一个模拟版本的通讯并在测试期间将其传入即可。您还可以将代码添加到您的模拟 class 以模拟某些测试条件 - 例如,对于接收一些无效数据的通信层。

您需要使具有方法 A() 的 class 依赖于方法 D() 使用的外部服务。您可以使用任何 DI 模式来执行此操作,尽管构造函数注入可能是最好的起点。

一旦您处于这种情况,D() 所依赖的外部服务就可以被伪造并注入到 class 中。测试现在由您通过假货的行为进行控制。

类似于:

class Thing
{
    private IExternalService _externalService;

    public Thing(IExternalService externalService)
    {
        _externalService = externalService;
    }

    public void A() { ... }

    public void D(string foo) 
    { 
        _externalService.DoSomeStuff();
    }
}

然后:

[Fact]
public void TestThisOut()
{
   var fakeExternalService = new MockFramework.CreateMock();
   fakeExternalService
       .ShouldDoSomethingWhen(s => s.DoSomeStuff())
       .IsCalled();

   var testThing = new Thing(fakeExternalService);

   testThing.A();

   Assert.That(testThing, Did.Some.Thing());
}

你需要mock方法D,我写了一个例子,使用Typemock Isolator,你看看:

class Methods
{
    public bool A(int foo)
    {
        bool result = false;
        if (foo > 0)
            result = B();
        else 
            result = C();
        D(foo);
        return result;
    }

    public void D(int foo) {throw new NotImplementedException();}

    public bool C() { return false;}

    public bool B() { return true;}
}

测试:

[TestMethod, Isolated]
public void TestIgnoreD()
{
    //Arrange
    Methods methods = new Methods();
    Isolate.WhenCalled(() => methods.D(0)).IgnoreCall();

    //Act
    bool result = methods.A(1);

    //Assert
    Assert.IsTrue(result);
}

我将它们全部放在一个 class 中只是因为我不知道您的代码中发生了什么。无论如何,Isolator 相当灵活,因为它允许从几乎所有地方模拟几乎所有东西。