使用设置和验证时在Moq中定义Mocks的最佳做法

时间:2019-01-10 08:37:12

标签: unit-testing mocking moq

Setup(...)Verify(...)有两种模式:

选项1

// Define mock
_textTransformerMock = new Mock<ITextTransformer>();

// Setup with Any String
_textTransformerMock.Setup(t => t.Transform(It.IsAny<string>())).Returns("transformed");

并且在验证时使用以下代码:

_textTransformerMock.Verify(t => t.Transform(It.Is<string>(s => s == "input")), Times.Once);



选项2

// Define mock
_textTransformerMock = new Mock<ITextTransformer>();

// Setup with the input string we expect
_textTransformerMock.Setup(t => t.Transform("input")).Returns("transformed");

并且在验证时使用以下代码:

_textTransformerMock.Verify(t => t.Transform("input"), Times.Once);




我的想法是,选项1更通用,在从单元测试中放置此代码时很有用,以便更多单个单元测试可以覆盖并提供不同的值。

选项2似乎对我有些锁定?

我希望有人可以分享他们的经验。

1 个答案:

答案 0 :(得分:1)

这是非常基于意见的,但这是我的意见。

没有用:

t => t.Transform(It.Is<string>(s => s == "input")

由于用于字符串的==的重载完全等于默认的EqualityComparer<string>,即顺序区分大小写的比较,因此较短:

t => t.Transform("input")

给出完全相同的结果。

否则,最好使用It.Is,例如It.Is<string>(s => s.StartsWith("in"))或其他任何东西。当然,它可以同时用于SetupVerify

另一个带有It.Is的示例是It.Is((string s) => string.Equals(s, "input", StringComparison.CurrentCultureIgnoreCase))


除此之外,这两个示例之间的区别在于您是Setup输入参数的所有值,还是Setup仅输入参数的期望值。我更喜欢后者(选项2)。如果您只希望一个参数是相关的,为什么还要为所有参数设置Moq?

您可以走得更远,并使用严格的模拟,即:

_textTransformerMock = new Mock<ITextTransformer>(MockBehavior.Strict);

这意味着Moq将不接受没有设置“匹配”的任何参数值。在这种情况下,如果您正在测试的代码向您的方法发送了意外的参数,您将获得异常。

话虽这么说,It.IsAny在无法提前告知将出现哪个值或何时无法提供具有相同值的对象(例如,引用类型的对象)时非常有用等式语义(与string不同),在这种情况下,您不能将与被测系统将使用的相同的 instance 传递给Moq。