初始化 DefaultHttpContext.Response.Body 到 MemoryStream 抛出 NullReferencePointer

Dan*_*nok 2 unit-testing middleware httpcontext exceptionhandler .net-core

我正在研究全局异常中间件的实现,我想用单元测试来覆盖中间件。下面你看看我走了多远。

这是单元测试的代码。

    [Fact]
    public async Task MethodName_StateUnderTest_ExpectedBehavior()
    {
        //Arrange
        IExceptionHandlerFeature exceptionHandlerFeature = new ExceptionHandlerFeature {Error = new NotFoundException()};

        IFeatureCollection features = new FeatureCollection();
        features.Set(exceptionHandlerFeature);

        var context = new DefaultHttpContext(features);
        context.Response.Body = new MemoryStream();
        //Act
        await ExceptionMiddleware.HandleException(context);

        //Assert
        context.Response.StatusCode.Should().Be((int) HttpStatusCode.NotFound);
    }
Run Code Online (Sandbox Code Playgroud)

这是 ExceptionMiddleware.Handle 方法的代码

public static async Task HandleException(HttpContext context)
{
    var contextFeature = context.Features.Get<IExceptionHandlerFeature>();

    if (contextFeature == null)
    {
        return;
    }

    if (contextFeature.Error is AppValidationException validationException)
    {
        context.Response.StatusCode = (int) HttpStatusCode.BadRequest;

        var failures = JsonConvert.SerializeObject(validationException.Failures);

        await context.Response.WriteAsync(
            new ErrorDetails
            {
                StatusCode = context.Response.StatusCode,
                Message = contextFeature.Error.Message,
                StackTrace = contextFeature.Error.StackTrace,
                Detail = failures
            }.ToString());

        return;
    }

    context.Response.StatusCode = (int) ResolveStatusCodeFromExceptionType(contextFeature.Error);
    context.Response.ContentType = "application/json";

    await context.Response.WriteAsync(
        new ErrorDetails
        {
            StatusCode = context.Response.StatusCode,
            Message = contextFeature.Error.Message,
            StackTrace = contextFeature.Error.StackTrace
        }.ToString());
}
Run Code Online (Sandbox Code Playgroud)

测试在线碾压

context.Response.Body = new MemoryStream();
Run Code Online (Sandbox Code Playgroud)

根据this question,一切都应该没问题,但我仍然无法初始化属性Body。

测试项目的目标框架是 .Net Core 3.0。 ExceptionMiddleware.cs在项目中设置了目标框架 .NET Standard 2.1,它是类库。

异常的堆栈跟踪非常短:

在 Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.set_Body(Stream value) 在 Itixis.Shared.API.Tests.ExceptionMiddlewareTests.d__0.MoveNext() 在 C:\Users\daniel.rusnok\source\repos\Itixis\Itixis。 Shared\Tests\Itixis.Shared.API.Tests\ExceptionMiddlewareTests.cs:line 35

Nko*_*osi 5

通过手动设置功能,您将删除默认设置的功能,这会导致您的 null 错误。

    public DefaultHttpContext()
        : this(new FeatureCollection())
    {
        Features.Set<IHttpRequestFeature>(new HttpRequestFeature());
        Features.Set<IHttpResponseFeature>(new HttpResponseFeature());
        Features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));
    }

    public DefaultHttpContext(IFeatureCollection features)
    {
        _features.Initalize(features);
        _request = new DefaultHttpRequest(this);
        _response = new DefaultHttpResponse(this);
    }
Run Code Online (Sandbox Code Playgroud)

来源

请注意在默认构造函数中添加的请求、响应和响应正文功能。

或者尝试通过添加所需的功能来重新创建默认构造函数中所做的事情,

IExceptionHandlerFeature exceptionHandlerFeature = new ExceptionHandlerFeature {Error = new NotFoundException()};

IFeatureCollection features = new FeatureCollection();
features.Set(exceptionHandlerFeature);
features.Set<IHttpRequestFeature>(new HttpRequestFeature());
features.Set<IHttpResponseFeature>(new HttpResponseFeature());
features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));

var context = new DefaultHttpContext(features);

//...
Run Code Online (Sandbox Code Playgroud)

或删除手动功能并使用默认构造函数。

[Fact]
public async Task MethodName_StateUnderTest_ExpectedBehavior() {
    //Arrange
    var context = new DefaultHttpContext();
    context.Response.Body = new MemoryStream();
    //Act
    await ExceptionMiddleware.HandleException(context);

    //Assert
    context.Response.StatusCode.Should().Be((int) HttpStatusCode.NotFound);
}
Run Code Online (Sandbox Code Playgroud)

它应该按预期工作。