我该如何在签名中具有可选参数而不显式指定它或使用重载的方法?


119

给定以下界面:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

尝试使用Moq模拟它:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);

在编译时给出以下错误:

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

我发现上述问题是Moq问题列表中的增强功能,并且似乎已分配给4.5版本(无论何时)。

我的问题是:鉴于上述情况不会很快得到解决,该怎么办?我的选择是只在每次模拟它时都显式设置可选参数的默认值(这种设置会破坏首先指定一个参数的目的)或创建没有布尔值的重载(就像我会做的那样)在C#4之前)?

还是有人遇到了更聪明的方法来克服这个问题?


5
仅将It.IsAny <bool>()指定为第二个参数是否合理?
Paul d'Aoust 2013年

一年半后这仍然是正确的..
Mukus

@Mukus,放心PR,兄弟。
IamDOM

Answers:


91

我相信您目前唯一的选择是将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
    {
        ...
    }
}

1
极好的答案;我已经继续并在我的模拟中明确指定了它,但是您的回答以一种非常清楚和合乎逻辑的方式确认了为什么我应该这样做。谢谢@克里斯。
2012年

9
更改默认参数“应该”中断测试。如果更改默认值,则没有测试失败可能是测试失败的迹象。代码可以使用默认值,但测试不使用默认值?
Pop Catalin

已经有一段时间了,但是当我尝试模拟一个接口(Dapper中的IDConnection)时,我已经尝试了Moq的这种方法,但仍然遇到相同的错误。有什么想法吗?示例行:嘲讽DB.Setup(x => x.Query <MyObject>(It.IsAny <string>(),It.IsAny <DynamicParameters>(),It.IsAny <IDbTransaction>(),false,600))。返回(新列表<MyObject>()); 最后两个值是我正在设置的方法上的可选参数。
Raelshark

4
啊!可怕的if (!x) {} else {}反模式:)
nicodemus13

1
@ nicodemus13是的,但是我试图使代码示例在问题中尽可能接近OP的示例。我不一定会提倡它:)
克里斯·曼特尔

8

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

public interface IFoo
{
    bool Foo(string a);

    bool Foo(string a, bool b);
}

现在这两种方法都可用,并且此示例可以工作:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);

2

使用Moq版本4.10.1,我可以执行以下操作

带接口:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

和模拟

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.