【问题标题】:Mocking IFlurl library methods using NSubstitute is throwing null reference exception使用 NSubstitute 模拟 IFlurl 库方法会引发空引用异常
【发布时间】:2018-11-29 21:57:46
【问题描述】:

我正在使用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();
        }
    }
}

我要测试的是,如果flurlRequest 抛出HttpFlurlException 类型的异常,那么它将抛出MyCustomException。我的想法是最小起订量 flurlrequest 并抛出异常。这就是我布置测试的方式:

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>();

代码运行时返回 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)

所以我看到它与标题有关......所以我也尝试通过添加来模拟它:

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

但我经常遇到同样的异常。我究竟做错了什么?

【问题讨论】:

    标签: c# mocking xunit nsubstitute flurl


    【解决方案1】:

    WithOAuthBearerToken 是一个扩展方法,这意味着它不能被 NSubstitute 直接模拟。当您在扩展方法上调用When..DoReturns 时,它将运行扩展方法的真实代码。 (我建议将NSubstitute.Analyzers 添加到您的测试项目中以检测这些情况。)

    在撰写本文时跟踪扩展方法实现,应该可以模拟 Headers 属性以抛出所需的异常,但我认为这拖入了太多的库内部知识,会导致在与特定实现紧密耦合的脆弱测试中(这是我们的目标是通过模拟来避免!)。

    我会非常警惕以这种方式模拟第三方库,正如我在 this answer 中概述的那样:

    另一种选择是在不同的级别上进行测试。我认为测试当前代码的摩擦是我们试图替代[第三方库]的细节,而不是我们为划分应用程序的逻辑细节而创建的接口。搜索“不要模拟你不拥有的类型”以获取更多关于为什么这可能是一个问题的信息(我在here 之前写过)。

    如果可能的话,我建议尝试改用Flurl's built-in testing support。这应该使您能够伪造您需要的行为,而无需有关 Flurl 内部实现的具体细节。

    【讨论】:

    • 这太棒了,我确实想知道 - 如果我在嘲笑这个时遇到了这么多麻烦,这本身就是一个警告信号,而且确实如此。扩展方法完全有意义,我一定会设置 NSub 分析器!谢谢,你帮了大忙!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 2015-03-08
    相关资源
    最近更新 更多