C# 跳过单元测试中的内部方法调用

C# 跳过单元测试中的内部方法调用,c#,unit-testing,xunit,C#,Unit Testing,Xunit,比如说,我有方法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,用一些假服务替换它?这突出了设计代码

比如说,我有方法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访问通信层。然后,类将引用其构造函数中的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
    }
}

然后只需创建一个模拟版本的comms,并在测试过程中传递它。您还可以向模拟类添加代码,以模拟某些测试条件,例如,对于接收一些无效数据的通信层。

您需要使具有方法
a()
的类依赖于方法
D()
使用的外部服务。您可以使用任何DI模式来实现这一点,尽管构造函数注入可能是最好的起点

在这种情况下,可以伪造所依赖的外部服务
D()
,并将其注入类中。测试现在由您通过假冒者的行为进行控制

比如:

class Thing
{
    private IExternalService _externalService;

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

    public void A() { ... }

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


您需要模拟方法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);
}

我把它们都放在一个类中,只是因为我不知道你的代码中发生了什么。无论如何,隔离器相当灵活,因为它允许模拟几乎所有地方的一切

您必须模拟
D
:如果输入
A
method,您无法避免调用
D
。是的,您必须模拟D。在任何一种情况下,您想要跳过
D
调用本身就是问题所在。您不想修改您的单元,否则它不是真正的测试。您的测试应该将代码单元视为一个黑匣子。“我给它这个输入,我期望这个输出。”是的,但是每次我运行单元测试时,在外部系统中下一个命令似乎不是一个好主意。
[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);
}