简体   繁体   中英

Expected invocation on the mock exactly 5 times, but was 0 times with properly mocked arguments

By reading this post and this and I understood that Mock should be used and result should be awaited.

Nevertheless, I still get the following exception:

Moq.MockException: Expected invocation on the mock exactly 5 times, but was 0 times: _ => _.SendAsync(Mock<IClientProxy:2>.Object, It.IsAny<string>(), It.IsAny<List<Notification>>(), CancellationToken)

Performed invocations:

MockIClientContextCommand:2 (_):

IClientContextCommand.SendAsync(MockIClientProxy:2.Object, "FooBarNotifications", [Notification], CancellationToken) IClientContextCommand.SendAsync(MockIClientProxy:2.Object, "FooBarNotifications", [Notification], CancellationToken) IClientContextCommand.SendAsync(MockIClientProxy:2.Object, "FooBarNotifications", [Notification], CancellationToken) IClientContextCommand.SendAsync(MockIClientProxy:2.Object, "FooBarNotifications", [Notification], CancellationToken) IClientContextCommand.SendAsync(MockIClientProxy:2.Object, "FooBarNotifications", [Notification], CancellationToken) IClientContextCommand.SendAsync(MockIClientProxy:2.Object, "FooBarNotifications", [Notification], CancellationToken) IClientContextCommand.SendAsync(MockIClientProxy:2.Object, "FooBarNotifications", [Notification], CancellationToken) IClientContextCommand.SendAsync(MockIClientProxy:2.Object, "FooBarNotifications", [Notification], CancellationToken) IClientContextCommand.SendAsync(MockIClientProxy:2.Object, "FooBarNotifications", [Notification], CancellationToken) IClientContextCommand.SendAsync(MockIClientProxy:2.Object, "FooBarNotifications", [Notification], CancellationToken)

Stack Trace: Mock.Verify(Mock mock, LambdaExpression expression, Times times, String failMessage)line337 Mock 1.Verify[TResult](Expression 1 expression, Times times)line34 FooBarServiceTests.SendAsync_ShouldBe_Called_N_Times()line105

So unit test look like this:

private readonly Mock<IClientContextCommand> _mockClientContextCommand;

// constructor
public FooTests()
{
    _mockClientContextCommand = new Mock<IClientContextCommand>();
    _mockClientContextCommand.Setup(x => 
        x.SendAsync(_mockClientProxy.Object, It.IsAny<string>(),
                    It.IsAny<Notification[]>(), CancellationToken.None));
        
    services.AddSingleton(_mockClientContextCommand.Object);
}

[Fact]
public async Task SendAsync_ShouldBe_Called_N_Times()
{
    await _hostedService.StartAsync(CancellationToken.None);
    await Task.Delay(1);
    await _hostedService.StopAsync(CancellationToken.None);

    int times = GetNotifications().Count();
    _mockClientContextCommand.Verify(
        _ => _.SendAsync(_mockClientProxy.Object, It.IsAny<string>(), 
                         It.IsAny<List<Notification>>(), CancellationToken.None),
        Times.Exactly(times));
}

And original method looks like this:

private async Task Send(CancellationToken stoppingToken)
{
    // ... the other code is omitted for the brevity
    foreach (IGrouping<string, Notification> group in messages.GroupBy(m => m.To))
    {
        IClientProxy connection = _hubContext.Clients.User(group.Key);
        List<Notification> notifies = group.ToList();
        
        await _clientContextCommand.SendAsync(connection, 
            "FooBarNotifications", notifies, stoppingToken);
    }
}

Interface method looks like this:

public interface IClientContextCommand
{
    Task SendAsync(IClientProxy clientProxy, string method, 
                   object? arg1, CancellationToken cancellationToken = default);
}

Does anybody know what is done wrong? Any help would be greatly appreciated

First you need to loosen the match parameters for the mocked member using It.IsAny so that the mocked member will be invoked. Since you setup/verification does not match what was actually invoked, you get the test failure.

Next, because this is an asynchronous member that doesn't appear to be returning a value, the member needs to return a completed task to allow the async code to flow to completion.

//... omitted for brevity

_mockClientContextCommand
    .Setup(_ => _.SendAsync(It.IsAny<IClientProxy>(), It.IsAny<string>(), It.IsAny<Notification[]>(), It.IsAny<CancellationToken>()))
    .Returns(Task.CompletedTask);

//... omitted for brevity

int times = GetNotifications().Count();
_mockClientContextCommand.Verify(
    _ => _.SendAsync(It.IsAny<IClientProxy>(), It.IsAny<string>(), It.IsAny<Notification[]>(), It.IsAny<CancellationToken>()),
    Times.Exactly(times));

The stack trace also shows that the expected count was supposed to be 5 but shows 10 invocations of SendAsync . This may need to be further investigated once you get the mock to match the expected invocation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM