如何在不显式指定或不使用重载的情况下对签名中带有可选参数的方法进行 Moq?

鉴于以下接口:

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版本(无论何时)。

我的问题是: 鉴于上述问题不会很快得到解决,我该怎么办?我的选项仅仅是在每次模拟它时显式设置可选参数的默认值(这种做法在一开始就违背了指定一个参数的要求) ,还是在没有 bool 的情况下创建一个重载(就像我在 C # 4之前会做的那样) ?

还是有人找到更聪明的方法来解决这个问题?

75501 次浏览

我相信您现在唯一的选择是在 Foo的设置中显式地包含 bool参数。

我不认为它违背了指定默认值的目的。默认值是方便调用代码的,但是我认为您应该在测试中显式地使用它。假设您可以省略指定 bool参数。如果将来有人将 b的默认值改为 true,会发生什么情况?这将导致测试失败(理应如此) ,但是由于隐藏的假设 bfalse,因此更难修复。显式指定 bool参数还有另一个好处: 它提高了测试的可读性。浏览它们的人会很快知道有一个 Foo函数接受两个参数。这至少是我的两分钱:)

至于每次模拟时指定它,不要重复代码: 在函数中创建和/或初始化模拟,这样您只有一个更改点。如果你真的想要,你可以通过复制 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
{
...
}
}

今天刚遇到这个问题,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);

使用 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);

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

在我的例子中,我的问题是我忘记为我的一个可选参数包含一个 It.IsAny<string>()调用。一旦我对所有参数(包括可选参数)进行了 It.Is...调用,它就能很好地进行编译。

假设您的方法是在接口中定义的,如下所示

Task<IDataResult<StripeSubModel>> CancelSub(List<string> list, bool? revert = false);

您可以像下面这样定义您的模拟服务

mockService.Setup(s => s.CancelSub(It.IsAny<List<string>>(), It.IsAny<bool>()))