Search code examples
c#asynchronousmoqdotnet-httpclientprotected

What is the correct syntax for mock.Verify for a protected setup?


I have the following unit test:

[TestMethod]
public async Task ShouldGetExperimentalValuePost()
{
    //arrange
    var avd = ActualVesselData.Parser.ParseJson(File.ReadAllText(".\\TestFiles\\ActualVesselData-TTN-online.json"));
    const string mlOutputForTestAvd = "{\"timestampAfterEpochMillis\": 1686235071742, \"raw_prediction\": 2.6, \"prediction\": 5.22, \"optimal_prediction\": 5.22, \"version_offline\": 38}";

    var mockMessageHandler = new Mock<HttpMessageHandler>();
    mockMessageHandler.Protected()
        .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
        .ReturnsAsync(new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent(mlOutputForTestAvd)
        });

    var mlClient = new MachineLearningClient(
        "http://localhost:7652/predict",
        new HttpClient(mockMessageHandler.Object));

    //act
    var result = await mlClient.DoProcessInput(avd);

    //assert
    Assert.IsNotNull(result, "Did not get a valid response object back from ML module");
    Assert.AreNotEqual((ulong)0, result.Timestamp, "Timestamp missing");
    mockMessageHandler.Protected().Verify("SendAsync()", Times.Once()
    
}

This compiles but give an exception with the following message:

System.ArgumentException: No protected method HttpMessageHandler.SendAsync() found whose signature is compatible with the provided arguments ().

I tried the following:

mockMessageHandler.Protected().Verify("SendAsync(ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>)", Times.Once()); 

That gives the same exception.

What is the correct syntax in this case?


Solution

  • In the Verify you should provide the same delegate as in the Setup

    mockMessageHandler.Protected().Verify(
        "SendAsync",
        Times.Once(),
        ItExpr.IsAny<HttpRequestMessage>(), 
        ItExpr.IsAny<CancellationToken>()
    );
    

    If you want to include the request url as well in the assertion then you can do the following

    mockMessageHandler.Protected().Verify(
        "SendAsync",
        Times.Once(),
        ItExpr.Is<HttpRequestMessage>(req =>
            req.RequestUri == new Uri("http://localhost:7652/predict") 
        ),
        ItExpr.IsAny<CancellationToken>()
    );