如何在没有明确指定或使用重载的情况下在其签名中具有可选参数的方法?

App*_*lus 108 c# unit-testing moq

给出以下界面:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}
Run Code Online (Sandbox Code Playgroud)

试图用Moq模拟它:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);
Run Code Online (Sandbox Code Playgroud)

在编译时出现以下错误:

表达式树可能不包含使用可选参数的调用或调用

我发现上面的问题是Moq问题列表中的一个增强,它似乎被分配到4.5版本(无论何时).

我的问题是:鉴于上述内容不会很快得到解决,我该怎么办?我的选项是否只是在每次模拟它时显式设置可选参数的默认值(哪种类型在第一时间指定一个失败)或创建没有bool的重载(就像我会做的那样)在C#4之前)?

或者有没有人遇到过更聪明的方法来克服这个问题?

Chr*_*tle 83

我相信你现在唯一的选择是明确地bool在设置中包含参数Foo.

我不认为它违背了指定默认值的目的.默认值是调用代码的便利,但我认为您应该在测试中明确.假设您可以省略指定bool参数.如果在未来,有人改变的默认值会发生什么btrue?这将导致失败的测试(这是理所当然的),但他们会因为隐藏的假设,即更难以解决bfalse.明确指定bool参数还有另一个好处:它提高了测试的可读性.经过它们的人会很快知道有一个Foo函数可以接受两个参数.这是我的2美分,至少:)

至于每次模拟时都指定它,不要复制代码:在函数中创建和/或初始化模拟,这样你只有一个变化点.如果你真的想要,你可以通过将Foo参数复制到这个初始化函数中来克服Moq明显的短缺:

public void InitFooFuncOnFooMock(Mock<IFoo> fooMock, string a, bool b = false)
{
    if(!b)
    {
        fooMock.Setup(mock => mock.Foo(a, b)).Returns(false);
    }
    else
    {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 更改默认参数"应该"中断测试.更改默认值时没有测试失败可能是测试错误的迹象.代码可以使用默认值但测试不是吗? (9认同)
  • Arrgggh!可怕的`if(!x){} else {}`反模式:) (3认同)
  • 更大的问题是,如果我有一个现有的测试库(有很多测试)并且我添加了一个可选参数,那么我不应该被要求将所有现有的测试更改为显式的 - 就像我不更改现有的测试一样生产代码。 (2认同)

CF5*_*CF5 9

使用 Moq 版本 4.10.1 我已经能够做到以下几点

带接口:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}
Run Code Online (Sandbox Code Playgroud)

和模拟

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>(), It.IsAny<bool>())).Returns(false);
Run Code Online (Sandbox Code Playgroud)

使用第一个参数可以解析对 Foo 的调用


Gil*_*man 8

刚刚遇到这个问题,Moq不支持这个用例.因此,对于这种情况,似乎重写该方法就足够了.

public interface IFoo
{
    bool Foo(string a);

    bool Foo(string a, bool b);
}
Run Code Online (Sandbox Code Playgroud)

现在两种方法都可用,这个例子可行:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);
Run Code Online (Sandbox Code Playgroud)