最小起订量在列出呼叫时说 0 呼叫?

Moq saying 0 calls when it lists a call?

我有一个使用 Moq 来模拟接口和验证调用的单元测试。这是测试代码:

[Fact]
public void NewBlank_InvokesManagerAdd()
{
    // ReSharper disable once AssignNullToNotNullAttribute
    var newPath = Path.Combine(_testSaveDirectory, "InvokeBlank.txt");

    _dbManagerMock.Setup(manager => manager.KeynoteDBs).Returns(new ObservableCollection<KeynoteDBVM>());
    _dialogMock.Setup(dialog => dialog.GetSaveFileDialogResult(It.IsAny<SaveFileDialogData>()))
               .Returns(newPath);

    _commands.CmdNewBlank.Execute(null);

    _dbManagerMock
        .Verify(manager => manager.AddDB(It.IsAny<KeynoteDB>(), It.IsAny<int>()),
                Times.Once);
}

然而,当我 运行 这个时,我得到这个测试失败:

Moq.MockException

Expected invocation on the mock once, but was 0 times: manager => manager.AddDB(It.IsAny<KeynoteDB>(), It.IsAny<int>())

Performed invocations:

   Mock<IDBManager:1> (manager):

      IDBManager.KeynoteDBs
      IDBManager.IsLoading = True
      IDBManager.ActiveDB
      IDBManager.AddDB(KeynoteDB, -1)
      IDBManager.ActiveDB
      IDBManager.IsLoading = False

   at Moq.Mock.Verify(Mock mock, LambdaExpression expression, Times times, String failMessage)
   at Moq.Mock`1.Verify[TResult](Expression`1 expression, Func`1 times)
   at KMCore_Tests.AppCommandsTests.NewBlank_InvokesManagerAdd() in *my path*\AppCommandsTests.cs:line 140

它不是在中间那个调用列表中列出该调用 1 次吗?怎么说它有 0 个调用?我错过了什么?我觉得我一定是错过了一些愚蠢的东西,但我看不到它......

编辑

好的,原来这是一个竞争条件问题,因为命令是异步的。命令执行调用一个 async void 方法,它必须在触发实际调用之前触发断言(或者至少我能想到的就是这些)。我在调用 execute 之后和断言之前放置了一个 await Task.Delay(500) 并且它现在正在通过。

有没有更好的方法来测试这种情况?这些命令本质上是按钮处理程序,所以根据我的理解,我认为 async void 在这里是正确的,但这意味着我不能在单元测试中等待它...

你应该avoid using async void anyway

async void 意味着火了就不管了。在您的情况下,您似乎不希望这样做,因为您需要等到异步方法完成。

如果您无法更改方法签名,您可以将 async void 方法的主体移动到 Task 返回方法。

例如,更改

public async void MyAsyncVoidMethod()
{
    await Task.Delay(500);

    MethodToBeCalled();
}

// Wait this task in unit test
public Task MyAsyncTask { get; private set; }

public async void MyAsyncVoidMethod()
{
    MyAsyncTask = MyAsyncTaskMethod();
    await MyAsyncTask;
}

public async Task MyAsyncTaskMethod()
{
    await Task.Delay(500);

    MethodToBeCalled();
}

或者,如果在命令完成后触发了任何事件,您也可以挂接到其中一个事件以在测试中保持通知状态。