0

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

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

4

1 回答 1

2

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

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

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

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

如果可能的话,我建议尝试使用Flurl 的内置测试支持。这应该使您能够伪造您需要的行为,而无需有关 Flurl 内部实现的具体细节。

于 2018-11-29T23:09:04.770 回答