DEV Community

Cover image for A couple ways I test my .NET APIs
Grant Hair
Grant Hair

Posted on

A couple ways I test my .NET APIs

Recently I have undertaken some work that required either building a whole new API or regression testing another API.

Through that experience I used 3 techniques to help build my confidence that what I was building actually worked, these are:

  • Unit Tests
  • In Memory API Tests with WebApplicationFactory<TEntryPoint>
  • Newman Tests

I will assume that everyone is familiar with unit tests in C# but just a quick note that my favourite framework is XUnit. I really enjoy the simplicity that it brings and allows me to get up and running super quickly.

In Memory API Tests with WebApplicationFactory<TEntryPoint>

tl;dr;

Check out this GitHub repo

https://github.com/GrantHair5/DotNetApiInMemoryTests

It has some pretty nice examples at a very basic level I'm taking lets get some fruit from a basket kinda stuff.

How?

The MVP of this test is WebApplicationFactory<TEntryPoint> it allows me to run an in-memory version of my API using TestServer this gives me access to a HttpClient and also my actual API. I can then send HTTP calls to my API as I would do in real life and assert the responses.

This is a great time to plug one of my favourite NuGet packages, FluentAssertions. It gives you the ability to assert test cases using plain English style language and makes complex test assertions a breeze.

Anyways,

To use WebApplicationFactory<TEntryPoint>

You would write something like the following:

public class ApiWebApplicationFactory : WebApplicationFactory<Startup>
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddTransient<IFruitBasket, StubbedFruitBasket>();
            });
        }
    }
Enter fullscreen mode Exit fullscreen mode

ApiWebApplicationFactory will become your new best friend.

Basically this is allowing us to override the default implementation of IFruitBasket with our new StubbedFruitBasket perfect for testing stuff with.

Then I inherit ApiWebApplicationFactory into my next best friend IntegrationTestBase which I use across each of my integration tests to get access to that sweet sweet in memory API.

 public class IntegrationTestBase : IClassFixture<ApiWebApplicationFactory>
    {
        protected readonly ApiWebApplicationFactory Factory;
        protected readonly HttpClient Client;

        public IntegrationTestBase(ApiWebApplicationFactory fixture)
        {
            Factory = fixture;
            Client = Factory.CreateClient();
        }
    }
Enter fullscreen mode Exit fullscreen mode

Then my tests look something like this:

 public class FruitApiShould : IntegrationTestBase
    {
        public FruitApiShould(ApiWebApplicationFactory fixture) : base(fixture)
        {
        }

        [Fact]
        public async Task Return_Two_Fruits_From_Stubbed_Basket()
        {
            var response = await Client.GetAsync("/api/Fruit/Basket");
            response.StatusCode.Should().Be(HttpStatusCode.OK);

            var basket = JsonConvert.DeserializeObject<IEnumerable<Fruit>>(await response.Content.ReadAsStringAsync());
            basket.Should().HaveCount(2);
        }
}
Enter fullscreen mode Exit fullscreen mode

If I want to override the stubbed implementation of my IFruitBasket I can do this by using my ApiWebApplicationFactory Factory from IntegrationTestBase to call Factory.WithWebHostBuilder() like so:

[Fact]
        public async Task Return_Four_Fruits_From_Real_Basket_Overriden_By_With_Web_Host_Builder()
        {
            // WithWebHostBuilder will allow us to override the base Client with any new services that we require.
            var client = Factory.WithWebHostBuilder(builder =>
            {
                builder.ConfigureServices(services =>
                {
                    services.AddTransient<IFruitBasket, FruitBasket>();
                });
            }).CreateClient();

            var response = await client.GetAsync("/api/Fruit/Basket");
            response.StatusCode.Should().Be(HttpStatusCode.OK);

            var basket = JsonConvert.DeserializeObject<IEnumerable<Fruit>>(await response.Content.ReadAsStringAsync());
            basket.Should().HaveCount(4);
        }
Enter fullscreen mode Exit fullscreen mode

Simple right 👍

Some things to note

1: services.ConfigureTestServices() is called after services.ConfigureServices() in the natural pipeline allowing us to override implementations or configuration or databases etc.

2: The above is obviously not a real life implementation and should be used as a guide, any terrible code is not a reflection of my real life performance 😂

Newman Tests

I have probably already bored you to death so I'll just drop a tl;dr; here

tl;dr;

Newman is an NPM package

It allows you to run many http requests from a postman collection

You can write JavaScripty style tests against your collections, for me these were:

Is my status code 200

Is my JSON response what I expect it to be

It is super cool and helpful.

It saved me so much time as I didn't have to hit over 120 endpoints individually.

Check out this link for more info:

https://learning.postman.com/docs/running-collections/using-newman-cli/command-line-integration-with-newman/

Hope this was of help

See Ya 👋

Top comments (1)

Collapse
 
lechus profile image
Leszek

Thank you very much.