简体   繁体   中英

Mocking IFlurl library methods using NSubstitute is throwing null reference exception

I am using flurl and I am trying to unit test the code below:

public class MyRestClient
{
    public async Task<T> Request<T>(IFlurlRequest flurlRequest)
    {
        try  
        {
            return await flurlRequest
                    .WithOAuthBearerToken("my-hardcoded-token")
                    .GetAsync()
                    .ReceiveJson<T>();  
        }
        catch(HttpFlurlException)
        {
            throw new MyCustomException();
        }
    }
}

What I want to test is that if flurlRequest throws an exception of type HttpFlurlException then it will throw MyCustomException . My idea is to moq the flurlrequest and throw an exception. This is how I layed out my test:

var moq = Substitute.For<IFlurlRequest>();
// Problem is with line below:
moq.When(x => x.WithOAuthBearerToken("dummy")).Do(x => { throw new HttpFlurlException(); } );

var myClient = new MyRestClient();

Func<Task> call = async () => { await myClient.Request<object>(moq); };

// FluentAssertions
call.Should().Throw<MyCustomException>();

The code when ran returns a NullReferenceException:

Exception has occurred: CLR/System.NullReferenceException
An exception of type 'System.NullReferenceException' occurred in 
Flurl.Http.dll but was not handled in user code: 'Object reference not 
set to an instance of an object.'
at Flurl.Http.HeaderExtensions.WithHeader[T](T clientOrRequest, String name, Object value)

So I see its something related to headers... so I tried also mocking that by adding:

var moq = Substitute.For<IFlurlRequest>();
moq.Headers.Returns(new Dictionary<string, object> { {"dummy", new {} };

But I'm constantly getting the same exception. What am I doing wrong?

WithOAuthBearerToken is an extension method, which means it cannot be mocked directly by NSubstitute. When you call When..Do or Returns on an extension method it will run the real code of the extension method. (I recommend adding NSubstitute.Analyzers to your test project to detect these cases.)

Tracing through the extension method implementation at the time of writing, it should be possible to mock the Headers property to throw the required exception, but I think this is dragging in much too much internal knowledge of the library and will result in brittle tests that are tightly coupled to that specific implementation (which is what we are aiming to avoid with mocking!).

I would be very wary of mocking out a third part library in this way, as I outlined in this answer :

The other option is to test this at a different level. I think the friction in testing the current code is that we are trying to substitute for details of [a third-party library], rather than interfaces we've created for partitioning the logical details of our app. Search for "don't mock types you don't own" for more information on why this can be a problem (I've written about it before here ).

If possible I suggest trying to use Flurl's built-in testing support instead. That should enable you to fake out the behaviour you need without requiring specific details about Flurl's internal implementation.

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