Giacomo d'Antonio
Giacomo d'Antonio

Reputation: 2275

Get a response value out of an IResult in ASP.Net's minimal API

I'm porting a project to the new minimal api of ASP.Net 6.

Right now I have something similar to this:

builder.MapGet("/hello", CiaoCiao);

IResult CiaoCiao()
{
    return Results.Ok("Ciao ciao!");
}

The reason for having the endpoint implementation in a separate function is that I want to write a unit test for it. But I'm having the following issue:

How do I get the response value (in this case the string "Ciao ciao!") out of the IResult?

So far I didn't find anything in the official documentation about that. There is a class Microsoft.AspNetCore.Http.Result.OkObjectResult which I could cast to. But that's internal to AspNetCore, so it's not accessible from my unit test project.

Upvotes: 2

Views: 8772

Answers (5)

Box Very
Box Very

Reputation: 438

In my case, I use:

Results.Text(formData, "application/json", statusCode: 200);

In UnitTest, try to cast result => Microsoft.AspNetCore.Http.HttpResults.ContentHttpResult, then use its ResponseContent property to get the content.

((Microsoft.AspNetCore.Http.HttpResults.ContentHttpResult)result).ResponseContent

Upvotes: 1

john nowlin
john nowlin

Reputation: 185

Not sure when the became available, but is now possible to get value from IResult.

[Fact]
public async Task CiaoCiao_ReturnsGreeting()
{
    var result = await CiaoCiao;

    var greeting = ((Microsoft.AspNetCore.Http.HttpResults.Ok<string>)result).Value;

    AssertEqual("Ciao ciao!", greeting);
}

I discovered it where the IEnumerableVisualizer displays the 'Expression' in the debug view for result Value property.

IEnumerableVisualizer View Option

IEnumerableVisualizer Expression

Upvotes: 0

Ben Wesson
Ben Wesson

Reputation: 639

It's also possible to do something simple like :

        [Fact]
        public void Then_it_returns_bad_request_result() =>
            _result.ToString().Should().EndWith("BadRequestObjectResult");

Not particularly satisfying, but does the job until I can investigate a better alternative using .NET7.

Upvotes: 1

Giacomo d&#39;Antonio
Giacomo d&#39;Antonio

Reputation: 2275

I was able to come up with a workaround using some code in the github issue linked by Martin Costello in his answer:

private static async Task<T?> GetResponseValue<T>(IResult result)
{
    var mockHttpContext = new DefaultHttpContext
    {
        // RequestServices needs to be set so the IResult implementation can log.
        RequestServices = new ServiceCollection().AddLogging().BuildServiceProvider(),
        Response =
        {
            // The default response body is Stream.Null which throws away anything that is written to it.
            Body = new MemoryStream(),
        },
    };

    await result.ExecuteAsync(mockHttpContext);

    // Reset MemoryStream to start so we can read the response.
    mockHttpContext.Response.Body.Position = 0;
    var jsonOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);
    return await JsonSerializer.DeserializeAsync<T>(mockHttpContext.Response.Body, jsonOptions);
}

Ugly, but seems to work.

Upvotes: 4

Martin Costello
Martin Costello

Reputation: 10843

This isn't possible with ASP.NET Core 6 as all the implementations of IResult are internal.

This is planned to be improved as part of ASP.NET Core 7.

Integration testing your code via the HTTP interface would be one way of testing your application's endpoints with ASP.NET Core 6 using the WebApplicationFactory<T> class (docs).

Upvotes: 4

Related Questions