是否存在 It.IsAny 不匹配值的情况?

Are there cases where It.IsAny does not match a value?

我在我的 MVVM WPF 项目中使用 Moq 库来模拟 Prism 库提供的接口,以便将它们作为我的视图模型的一部分进行单元测试。具体来说,我有一个使用 Prisms IDialogService.

的视图模型

我想对执行特定命令是否打开带有特定参数的特定对话进行单元测试。这是我为此所做的:

 // Arrange

 var mockDialogService = new Mock<IDialogService>();
 var viewModel = SetupViewModel(mockDialogService.Object);

 // Act

 viewModel.OpenNewStockDialogueCommand.Execute();

 // Assert

 mockDialogService.Verify(s => s.ShowDialog(
   It.IsAny<string>(),
   It.IsAny<IDialogParameters>(),
   It.IsAny<Action<IDialogResult>>()),
   Times.Once);

相关部分发生在我测试的断言部分。 ShowDialog 方法采用以下参数:

  1. 姓名:string
  2. 参数:IDialogParameters
  3. 回调:Action<IDialogResult>

Here is some documentation on the Prism Dialogservice, for those interested

现在,据我了解,如果我在调用 Verify 方法之前调用 ShowDialog 方法,无论我实际将哪些参数传递给它, Verify 方法应该无一例外地完成。正如我在论坛上所猜到的那样,它实际上确实抛出了一个异常,并向我显示了以下消息:

Message: 
    Moq.MockException : 
    Expected invocation on the mock once, but was 0 times: s => s.ShowDialog(It.IsAny<string>(), It.IsAny<DialogParameters>(), It.IsAny<Action<IDialogResult>>())
    
    Performed invocations:
    
       Mock<IDialogService:1> (s):
    
          IDialogService.Show("NewStockDialogue", ?CurrentStock=FHAA.Stock, Action<IDialogResult>)

好了。这对我来说很奇怪。似乎该方法确实被调用,如“执行的调用”所示,但参数不匹配。有人知道为什么吗?

同样重要的是要注意我实际上不想把 It.IsAny 放在这里,我实际上有一些我想测试的半固定值,但是因为它不适用于 It.IsAny,我有什么希望让它在限制下工作。

非常感谢您抽出宝贵时间,非常感谢您的帮助!

编辑

原来我实际上是在测试 IDialogService.Show 而不是 ShowDialog。这就是问题所在

dialogService.Show(DialogueNames.NewStock, parameters, OnNewStockDialogueClosed);

正如@Nkosi 已经指出的那样,您正在调用 Show 方法,但请验证是否调用了 ShowDialog。错误消息中明确说明了这一点。决定要使用哪一个并调整测试或视图模型。

It is also important to note that I dont actually want to put It.IsAny here, I actually have some semi-fixed values I want to test for [...]

您可以使用 It.Is<T> 检查参数的条件,而不是 It.IsAny<T>。你只需要传递一个 Expression<Func<T, bool>>,这是一个谓词的表达式。此谓词检查相应参数的正确性,returns 结果为 bool。看看这个例子。

viewModel.ShowDialog("My dialog name", new DialogParameters {{"Key 1", "Value 1"}}, result => testInt = 32);

我在视图模型中调用了这个 ShowDialog。我可以验证,如果参数是这样的。

var expectedDialogParameters = new DialogParameters {{ "Key 1", "Value 1" }};
mockDialogService.Verify(s => s.ShowDialog(
   It.Is<string>(name => name == "My dialog name"),
   It.Is<DialogParameters>(dialogParameters => dialogParameters.SequenceEqual(expectedDialogParameters)),
   It.IsAny<Action<IDialogResult>>()),
   Times.Once);

对于对话框的名称,我只是在 lambda 中检查 string 是否相等。对于对话框参数,我使用了我预期的对话框参数的一个实例。我将 DialogParametersSequenceEqual 进行比较,因为它们被实现为 IEnumerable<KeyValuePair<string, object>>,因此是一个键值对列表。我不能使用 Equals,因为它没有在 DialogParameters 中被覆盖,并且只会检查 引用相等性 ,这会失败。此外,在 It.Is 中使用 DialogParametersIDialogParameters 只是 DialogParameters 的接口,它 而不是 实现 IEnumerable<KeyValuePair<string, object>>,因此您无法与 SequenceEqual 进行比较。最后但同样重要的是,您 可以 验证对话结果操作,但这可能并不容易。您可以改为检查您的视图模型,因为正在执行的操作而发生了什么变化。