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>();
});
}
}
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();
}
}
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);
}
}
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);
}
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:
Hope this was of help
See Ya 👋
Top comments (1)
Thank you very much.