简体   繁体   English

使用NSubstitute模拟IFlurl库方法会引发空引用异常

[英]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: 我正在使用flurl,并且尝试对以下代码进行单元测试:

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 . 我要测试的是,如果flurlRequest抛出HttpFlurlException类型的异常,则它将抛出MyCustomException My idea is to moq the flurlrequest and throw an exception. 我的想法是发送flurlrequest并引发异常。 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: 运行时的代码返回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. WithOAuthBearerToken是一个扩展方法,这意味着NSubstitute无法直接对其进行模拟。 When you call When..Do or Returns on an extension method it will run the real code of the extension method. 当您在扩展方法上调用When..DoReturns时,它将运行该扩展方法的实际代码。 (I recommend adding NSubstitute.Analyzers to your test project to detect these cases.) (我建议将NSubstitute.Analyzers添加到测试项目中以检测这些情况。)

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!). 在编写本文时跟踪扩展方法的实现,应该可以模拟Headers属性以引发所需的异常,但是我认为这会拖累过多的库内部知识,并且会导致脆弱的测试。与该特定实现紧密结合(这是我们旨在通过模拟避免的目标!)。

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. 如果可能,我建议尝试使用Flurl的内置测试支持 That should enable you to fake out the behaviour you need without requiring specific details about Flurl's internal implementation. 这应该使您能够伪造所需的行为,而无需有关Flurl内部实现的特定详细信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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