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

Posted

技术标签:

【中文标题】如何在没有明确指定或使用重载的情况下 Moq 在其签名中具有可选参数的方法?【英文标题】:How do I Moq a method that has an optional argument in its signature without explicitly specifying it or using an overload? 【发布时间】:2012-10-09 01:57:27 【问题描述】:

给定如下界面:

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 的问题列表中以 enhancement 的形式出现,并且似乎已分配给 4.5 版本(无论何时)。

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

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

【问题讨论】:

只为第二个参数指定 It.IsAny() 是否合理? 一年半之后,这仍然是真的.. @Mukus,请随意放弃 PR,兄弟。 【参考方案1】:

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

我不认为它违背了指定默认值的目的。默认值是为了方便调用代码,但我认为你应该在测试中明确。假设您可以省略指定 bool 参数。如果将来有人将b 的默认值更改为true,会发生什么?这将导致测试失败(这是理所当然的),但由于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
    
        ...
    

【讨论】:

优秀的答案;我已经继续并在我的模拟中明确指定了它,但是您的回答以非常清晰和合乎逻辑的方式证实了为什么我应该这样做。谢谢,@Chris。 更改默认参数“应该”中断测试。更改默认值时没有测试失败可能是测试不佳的标志。代码可能使用默认值,但测试不使用? 已经有一段时间了,但是我在尝试模拟接口(Dapper 中的 IDConnection)时尝试了这种方法,但我仍然遇到同样的错误。任何想法为什么?示例行:mockDB.Setup(x => x.Query(It.IsAny(), It.IsAny(), It.IsAny(), false, 600))。返回(新列表());最后两个值是我正在设置的方法的可选参数。 啊!可怕的if (!x) else 反模式:) @nicodemus13 是的,但我试图使代码示例尽可能接近问题中的 OP 示例。我不一定会提倡它:)【参考方案2】:

今天刚遇到这个问题,起订量不支持这个用例。 因此,对于这种情况,似乎覆盖该方法就足够了。

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

【讨论】:

【参考方案3】:

使用 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的调用

【讨论】:

【参考方案4】:

就我而言,我的问题是我忘记为我的一个可选参数包含一个It.IsAny&lt;string&gt;() 调用。一旦我对我的所有参数(包括可选参数)进行了It.Is... 调用,它就编译得很好。

【讨论】:

编译正常与实际被模拟方法覆盖不同。

以上是关于如何在没有明确指定或使用重载的情况下 Moq 在其签名中具有可选参数的方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不明确指定主键的情况下使用 Dapper Extensions 将对象插入 PostGreSql?

除非明确指定,否则如何在默认情况下将 Spring Data JPA 从 camelCase 命名为 snake_case?

如何在没有重载数据的情况下更改uitableview中的高度单元格

如何在不使用 transform.Rotate 的情况下在其本地或世界轴上旋转带有第二个四元数的四元数?

如何验证在 Moq 中没有调用该方法?

如何使网站在其名称中没有 www 前缀的情况下正常工作?