Alexander Hardy
Alexander Hardy

Reputation: 63

NSubstitute for Protected Methods

I am migrating existing unit tests from Moq to NSubstitute.

For most cases it has been a very smooth transition, but when it comes to mocking an HttpMessageHandler (SendAsync) there was a pretty slick way to get into the private method on moq.

(previous example for moq for setting up return with argument expressions)

protected void SetupHandler<T>(Expression<Func<HttpRequestMessage, bool>> match, HttpStatusCode httpStatusCode,
   T body)
    {
        SetupHandlerStringResponse(match, httpStatusCode, JsonConvert.SerializeObject(
            body), "application/json");
    }

    protected void SetupHandlerStringResponse(Expression<Func<HttpRequestMessage, bool>> match, HttpStatusCode httpStatusCode,
string body, string contentType)
    {
        MockHttpMessageHandler.Protected()
            .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.Is(match), ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = httpStatusCode,
                Content = new StringContent(body, Encoding.UTF8, contentType)
            });
    }

    protected void VerifyHandler(Expression<Func<HttpRequestMessage, bool>> match, Times times)
    {
        MockHttpMessageHandler.Protected().Verify("SendAsync", times, ItExpr.Is(match), ItExpr.IsAny<CancellationToken>());
    }

using SetupHandler and VerifyHandler I could easily call the setup/verify steps in my code like this...

SetupHandler(t => t.Method == HttpMethod.Get, HttpStatusCode.OK, lookup);

var response = await Class.DoSomething();

Assert.NotNull(response);

VerifyHandler(t => t.Method == HttpMethod.Get && t.RequestUri.PathAndQuery == $"/api/endpointName", Times.Once());

As far as I know, there is no direct case in NSubstitute that supports this.

I have found many write-ups online following issues like this, and there always seems to be some sort of issue with parameters. I have seen this approach pop up several times for replacing this 'Protected' accessor

This way seems like would work if there were no params OR no param validation needed

But in my case, it is not useful to me if I cannot use the Arg.Is expressions to valdiation the inputs and still mock an output for the HttpMessageHandler.

Any Help/Ideas would be appriciated.

Upvotes: 0

Views: 383

Answers (1)

Alexander Hardy
Alexander Hardy

Reputation: 63

I think I found an acceptable answer to my own question.

found THIS article by Daniel Ward (Dan in a can) that outlines 3 potential solutions.

Option 1: create a fake HttpMessageHandler class

This was not really it for me, I still needed to be able to use NSubstitute to match a response to the inputs.

Option 2: Set up calls to the protected SendAsync() method using reflection in extension methods

THIS was exactly what I was looking for. Once plugged in correctly, it seems to have everything I need to replace my current set up(s) with MOQ

Option 3: MockHttpMessageHandler from RichardSzalay.MockHttp

This option also worked, and seemed to work very well. Avoided this one though simply to pass on introducing a new package to the solution.

Hopefully pointing my example to this article helps someone later on, and even more optimistically, NSubstitute introduces something like this into the library so it comes along with it in a .Protected() like it does in Moq.

Upvotes: 1

Related Questions