在 C# 单元测试中,如何相当于 Java 的 Spy(而不是 Mock)来仅模拟类上的某些方法?

Don*_*mmy 8 .net c# unit-testing mocking visual-studio

在 Java 中,当我有一个类调用其他类的静态方法时,我总是封装它,这样我就可以测试它,而无需实际访问该真实资源。例如:

public class HasSomeStaticCall
{
    public HasSomeStaticCall()
    {
        this.something = callStaticThing();
    }

    protected String callStaticThing()
    {
        return SomeThirdParty.getFromStaticMethod();
    {
}
Run Code Online (Sandbox Code Playgroud)

在Java 中,我可以使用Spy而不是Mock然后使用除该方法之外的所有实际方法。

例子:

public void test()
{
    HasSomeStaticCall obj = Mockito.spy( HasSomeStaticCall.class );

    //Only mock this one method
    Mockito.doReturn( "SomeValue" ).when( obj ).callStaticThing();
}
Run Code Online (Sandbox Code Playgroud)

我将如何在 C# 中执行此操作?(我使用的是.Net Framework 4.7.x,而不是.Net Core)

Mih*_*dov 6

使用 C# 模拟框架,您不会有像间谍这样的概念。您可以做的是创建一个模拟并安排它调用原始代码,其中调用静态方法的方法可以返回您想要的值。您应该记住,免费的 C# 模拟框架仅适用于接口和虚拟方法。因此,如果您的情况是安排一个返回值的公共虚拟方法,那么这就是如何完成的。我将在接下来的示例中使用免费版本JustMock :

HasSomeStaticCall obj = Mock.Create<HasSomeStaticCall>(Behavior.CallOriginal);
Mock.Arrange(() => obj.callStaticThing()).Returns("SomeValue");

var actual = obj.callStaticThing();

Assert.AreEqual("SomeValue", actual);
Run Code Online (Sandbox Code Playgroud)

JustMock 有一个商业版本,允许您模拟非公共方法,例如代码中的受保护方法。对于这种方法来说,安排应该是这样的:

HasSomeStaticCall obj = Mock.Create<HasSomeStaticCall>(Behavior.CallOriginal);

Mock.NonPublic.Arrange<string>(obj, "callStaticThingProtected").Returns("SomeValue");
var actual = obj.MethodThatCallsTheProtectedcallStaticThing();

Assert.AreEqual("SomeValue", actual);
Run Code Online (Sandbox Code Playgroud)

当然,应该有一种方法可以调用受保护的方法。这就是我使用 MethodThatCallsTheProtectedcallStaticThing 的原因。但是,如果您有其他执行此方法的方式,则不需要这样做。

我注意到,在您的场景中,您实际上是使用调用静态方法的方法的结果来设置构造函数中字段的值。大多数 C# 模拟框架将允许您使用三种可能的选项来处理构造函数的执行:调用原始构造函数、不调用构造函数以及执行其他操作而不是调用构造函数。它们都不允许您在创建模拟之前提前安排 HasSomeStaticCall 类的一些实例方法。这是一个问题,因为构造函数正在执行您想要保留的操作。JustMock 允许您模拟静态方法。以下是您的场景的示例:

Mock.Arrange(() => SomeThirdParty.getFromStaticMethod()).Returns("SomeValue");

HasSomeStaticCall obj = new HasSomeStaticCall();
var actual = obj.Something;

Assert.AreEqual("SomeValue", actual);
Run Code Online (Sandbox Code Playgroud)

我创建了一个 Something 属性来访问该字段的值,如果您有其他方法在需要时验证该值,则不需要该属性。

如果您对 JustMock 感兴趣,可以查看JustMock 和 JustMock Lite 的功能比较表

免责声明。我是负责JustMock的开发人员之一。