Integration testing a web API [Answered]

Tthinker2279/4/2022
Just for the sake of the learning experience, I wanna write some integration tests for the endpoints in my web API. What's the typical way of doing this? Do you have to use some kind of HttpClient setup to call the API as if using HTTP or is there some better way of just testing the endpoints individually? For context, all of my endpoints return Task<IResult>, so I (probably) can't just test the direct return value of each endpoint method.
Ppox9/4/2022
Do you want to test the Http calls? Or do you just want to test the logic of the endpoint?
We do both, we start up a testhost and do http calls at it. We also allow you to replace instances with mocks if you want to not have any external calls etc. You can also just create a Controller with all the dependencies and use the functions directly
Tthinker2279/4/2022
Test the logic of the endpoint
Tthinker2279/4/2022
I don't think I care about testing the HTTP calls...?
Tthinker2279/4/2022
I suppose I can do something like
var result = await Endpoints.GetSomethingAsync();
var ok = Assert.IsType<OkObjectResult>(result);
Assert.Equal(StatusCodes.Status200OK, ok.StatusCode);
Assert.Equal(expected, ok.Value);
Tthinker2279/4/2022
Seems like otherwise you need to test the entire pipeline with WebApplicationFactory
Ppox9/4/2022
Yeah
Tthinker2279/4/2022
That's... annoying
Tthinker2279/4/2022
Is there at least some way to not have to write the endpoint route and instead have it be inferred from the method?
Ppox9/4/2022
We generate a client from the openapi spec that has models and routes etc, might be very overkill
Ppox9/4/2022
I've seen ppl make consts and helper methods for setting and calling the routes
Tthinker2279/4/2022
... you can't access Microsoft.AspNetCore.Http.Result.OkObjectResult???
Tthinker2279/4/2022
why tf
Tthinker2279/4/2022
Why do you need to do so much stuff to write some tests :catlost:
Ppox9/4/2022
Most stuff you need to setup once, then the actual tests are small
Ppox9/4/2022
Are you doing the httpclient route?
Ppatrickk9/4/2022
i don't know what you're actually trying to achieving by testing this
Ppatrickk9/4/2022
all you seem to be doing is testing asp.net core
Tthinker2279/4/2022
I wanna test my endpoint methods?
Ppatrickk9/4/2022
what's the goal of the test
Ppox9/4/2022
Test the endpoint methods
Ppatrickk9/4/2022
that's not a goal
Ppatrickk9/4/2022
that's an activity
Tthinker2279/4/2022
... testing that they do the correct thing
SStarlk9/4/2022
to see if they work as expected?
Ppatrickk9/4/2022
people writing pointless tests lead to maintaining absolutely worthless code, if you're doing this for experience learn what makes a good test
Tthinker2279/4/2022
wow, thanks :catpog:
Ppatrickk9/4/2022
not sure if sarcasm, the point being most people end up preaching 100% code coverage and end up spamming tests which have little worth in the bigger picture of actually maintaining a project
Tthinker2279/4/2022
Well what should I be doing then? I wanna learn how to write tests for stuff, because that's what I've seen other people do.
Ppox9/4/2022
Agreed, doing tests for testing sake is bad
Tthinker2279/4/2022
I can absolutely see that
Ppatrickk9/4/2022
that depends on the project - imitating unit tests just because you've seen them defeats the purpose of unit tests
Ppox9/4/2022
But we tend to unit test logic heavy code and do some integration tests for the "code clue"
Tthinker2279/4/2022
This is literally my first web API project and it's ridiculously small, really I don't need tests but I thought it'd be a good opportunity to learn anyway
Ppatrickk9/4/2022
ok but you've built the API to do something, yes?
Tthinker2279/4/2022
Yes
Ppatrickk9/4/2022
and you'd have an idea of who would consume the API?
Tthinker2279/4/2022
Yes
Ppatrickk9/4/2022
the integration tests should be modelled around that - "as a user of this API, when I do X I should get Y and be able to do Z"
Tthinker2279/4/2022
Okay so I shouldn't be testing the individual endpoint methods but rather the HTTP routes?
Ppatrickk9/4/2022
ive come in with a little less context to be honest, but if you're going for an integration test that sounds more like what you should be doing
Tthinker2279/4/2022
Right
MMMayor McCheese9/4/2022
I usually do both tbh
MMMayor McCheese9/4/2022
My primary motivations for web application factory tests is to test I get 401s back
MMMayor McCheese9/4/2022
This can still be done with controller tests in a manner
Tthinker2279/4/2022
Okay so I'm getting an exception System.InvalidOperationException : The server has not been started or no web application was configured. when calling WebApplicationFactory<TEntryPoint>.CreateClient()
Tthinker2279/4/2022
why is this so damn hard :catlost:
Tthinker2279/4/2022
That's the article I'm reading
Tthinker2279/4/2022
Okay now it works when just using the first example of var application = new WebApplicationFactory<Program>()
Ppox9/4/2022
ingame currently so cant look 😄
Tthinker2279/4/2022
This is my thing currently
[Fact]
public async Task GetListsAsync_ReturnsIds() {
    var ids = Enumerable.Range(0, 10)
        .Select(_ => Guid.NewGuid())
        .ToArray();

    Mock<ITodoService> todoServiceMock = new();
    todoServiceMock
        .Setup(x => x.GetListIdsAsync())
        .ReturnsAsync(ids);
    var todoService = todoServiceMock.Object;

    var a = new WebApplicationFactory<Program>()
        .WithWebHostBuilder(builder => builder
            .ConfigureServices(services => services
                .AddScoped(todoService)));
    var client = a.CreateClient();

    var response = await client.GetAsync("/getLists");
    Assert.True(response.IsSuccessStatusCode);
    var value = await response.Content.ReadFromJsonAsync<List<Guid>>();
    Assert.Equal(ids, value);
}
Tthinker2279/4/2022
It works and I guess it's fairly compact
Ppox9/4/2022
Tthinker2279/4/2022
How would you add a mock service to that?
Ppox9/4/2022
We use some logic so you can replace services at startup of the host
Tthinker2279/4/2022
Also when you say "we", do you mean your company?
Ppox9/4/2022
Yeah
Ppox9/4/2022
Swedish digital healthcare
Ppox9/4/2022
Also do you need to mock the todoSerivce? Might be worth doing a
Tthinker2279/4/2022
wait we're both swedes :catsip:
Tthinker2279/4/2022
Once again more of a case of just wanting to try what I've seen others do because I wanna learn
Tthinker2279/4/2022
Plus it's kind of useful since some endpoints only need to call specific methods in the service.
Ppox9/4/2022
Are you using xunit?
Tthinker2279/4/2022
yep
Ppox9/4/2022
If you want we could perhaps jump in to a call and look at it, maybe not tonight tho
Ppox9/4/2022
You could also do something simplier
Tthinker2279/4/2022
Mind showing an example?
Tthinker2279/4/2022
also completely unrelated, is your pfp literally the ica logo?
Ppox9/4/2022
Yeah
Tthinker2279/4/2022
love it
Ppox9/4/2022
Maybe something like this
public class CustomWebApplicationFactory<TStartup>
    : WebApplicationFactory<TStartup> where TStartup : class
{

    private readonly Mock<ITokenService> _mock;

    public CustomWebApplicationFactory(Mock<ITokenService> mock)
    {
        _mock = mock;
    }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            services.ReplaceWithInstance(_mock.Object);
        });
    }
}


Im sure there are better ways
Ppox9/4/2022
You could also just move some logic to the ctor a function to keep it simple
Ppox9/4/2022
    Mock<ITodoService> todoServiceMock = new();
    var a = new WebApplicationFactory<Program>()
        .WithWebHostBuilder(builder => builder
            .ConfigureServices(services => services
                .AddScoped(todoServiceMock.Object)));


Do this and save the application and mock in a readonly field
Ppox9/4/2022
I gtg
Ppox9/4/2022
This got messy but 😄
Tthinker2279/4/2022
I need to go to sleep anyway
Tthinker2279/4/2022
Thanks for the assistance :catsip:
MMMayor McCheese9/5/2022
Net6?
MMMayor McCheese9/5/2022
If you’re using top level statements there’s some things you have to do for web application factory
Tthinker2279/5/2022
yes
AAccord9/6/2022
✅ This post has been marked as answered!