单元测试ASP.NET Core HttpResponse.OnStarting()

我有一个ASP.NET Core中间件,负责将标头添加到响应中。按照以下最佳实践,我将在的上下文中执行标头更改HttpResponse.OnStarting(Func<Task>),以确保在将响应刷新到客户端之前立即执行回调。


public class ResponseHeadersMiddleware

{

    private readonly RequestDelegate _next;


    public ResponseHeadersMiddleware(RequestDelegate next)

    {

        _next = next;

    }


    public async Task Invoke(HttpContext context)

    {

        context.Response.OnStarting(() =>

        {

            context.Response.Headers.Add("X-Some-Header", "Foobar");


            return Task.CompletedTask;

        });


        // Pass the request through the pipeline 

        await _next(context);

    }

}

这可以按预期工作,但是我不确定为这种实际触发的中间件编写单元测试的最佳方式HttpResponse.OnStarting()。我唯一能想到的就是使用aMicrosoft.AspNetCore.TestHost构建TestServer集成中间件并执行完整请求管道的a。尽管功能正常,但它更像是一个集成测试,而不是真正的单元测试。


[Fact]

public async Task when_adding_response_headers()

{

    // ARRANGE

    var subject = new TestServer(new WebHostBuilder()

        .UseStartup<TestStartup<ResponseHeadersMiddleware>>());


    // ACT

    var response = await subject.CreateClient()

        .SendAsync(new HttpRequestMessage(HttpMethod.Get, "/")); // middleware fires for all requests


    // ASSERT

    Assert.True(response.Headers.TryGetValues("X-Some-Header", out var someHeader));

    Assert.Equals("Foobar", someHeader.FirstOrDefault()

}


private class TestStartup<TMiddleware> where TMiddleware : class

{

    public void ConfigureServices(IServiceCollection services)

    {

        RequestDelegate requestDelegate = context => Task.FromResult(0);


        services.AddSingleton(requestDelegate);

        services.AddSingleton<TMiddleware>();

    }


    public void Configure(IApplicationBuilder app)

    {

        dynamic middleware = app.ApplicationServices.GetService(typeof(TMiddleware));


        app.Use(async (ctx, next) =>

        {

            await middleware.Invoke(ctx);

            await next();

        });

    }

}

有没有一种方法可以HttpResponse.OnStarting()在没有进行端到端集成测试的情况下触发传递给我的中间件的HttpContext?


手掌心
浏览 331回答 2
2回答

慕盖茨4494581

在仓库中进行一些挖掘并查看了他们的一些测试之后,我想到了这个想法,以利用受控上下文的响应功能。这意味着需要找到一种方法来捕获传递给的回调OnStarting。决定尝试通过虚拟响应功能来获取它。private class DummyResponseFeature : IHttpResponseFeature {&nbsp; &nbsp; public Stream Body { get; set; }&nbsp; &nbsp; public bool HasStarted { get { return hasStarted; } }&nbsp; &nbsp; public IHeaderDictionary Headers { get; set; } = new HeaderDictionary();&nbsp; &nbsp; public string ReasonPhrase { get; set; }&nbsp; &nbsp; public int StatusCode { get; set; }&nbsp; &nbsp; public void OnCompleted(Func<object, Task> callback, object state) {&nbsp; &nbsp; &nbsp; &nbsp; //...No-op&nbsp; &nbsp; }&nbsp; &nbsp; public void OnStarting(Func<object, Task> callback, object state) {&nbsp; &nbsp; &nbsp; &nbsp; this.callback = callback;&nbsp; &nbsp; &nbsp; &nbsp; this.state = state;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; bool hasStarted = false;&nbsp; &nbsp; Func<object, Task> callback;&nbsp; &nbsp; object state;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; public Task InvokeCallBack() {&nbsp; &nbsp; &nbsp; &nbsp; hasStarted = true;&nbsp; &nbsp; &nbsp; &nbsp; return callback(state);&nbsp; &nbsp; }}在测试中,我将在上设置功能HttpContext,然后直接测试中间件。[Fact]public async Task when_adding_response_headers() {&nbsp; &nbsp; // ARRANGE&nbsp; &nbsp; var feature = new DummyResponseFeature();&nbsp; &nbsp; var context = new DefaultHttpContext();&nbsp; &nbsp; context.Features.Set<IHttpResponseFeature>(feature);&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; RequestDelegate next = async (ctx) => {&nbsp; &nbsp; &nbsp; &nbsp; await feature.InvokeCallBack();&nbsp; &nbsp; };&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; var subject = new ResponseHeadersMiddleware(next);&nbsp; &nbsp; // ACT&nbsp; &nbsp; await subject.Invoke(context);&nbsp; &nbsp; // ASSERT&nbsp; &nbsp; var response = context.Response;&nbsp; &nbsp; Assert.True(response.Headers.TryGetValues("X-Some-Header", out var someHeader));&nbsp; &nbsp; Assert.Equals("Foobar", someHeader.FirstOrDefault()}在中间件中等待请求委托时,让请求委托调用来捕获该回调
打开App,查看更多内容
随时随地看视频慕课网APP