C
C#2w ago
Raso

Issue with xUnit and Integration Test

Hello everyone, I'm trying to do my first tests inside my template. I have already done Unit tests, mocking the dependencies, now I wanted to do some Integration tests to test the minimal apis flow, if they work as expected. For this test project I'm using Testcontainers.PostgreSql which allow me to use a docker container to run database, and I'm using xUnit for the tests. Said so, I'm having a small issue: I know that tests shouldn't depend one from another, but let's say I need to test those minimal apis which have all the CRUD, included the DeleteAll. I need those tests to run with a certain order, because if DeleteAll for some reason ends before the GetAll, the GetAll test will not find anything inside the database. So I need DeleteAll to run as last one. What do you think about that? What's your approach for this problem? And how can I actually implement an order in xUnit?
7 Replies
Raso
RasoOP2w ago
This is, just for context, the GetAll test and the DeleteAll test:
C#

public class TodoTests : BaseIntegrationTest
{
private readonly List<TodoEntity> _todoEntitiesSeed =
[
new() { Title = "Test Todo 1", Description = "Description 1" },
new() { Title = "Test Todo 2", Description = "Description 2" },
new() { Title = "Test Todo 3", Description = "Description 3" },
];

private const string BaseEndpoint = "/api/todo";


...


[Fact]
public async Task GetAll_ShouldReturn_AllEntities()
{
// Arrange
var minLengthResult = _todoEntitiesSeed.Count;

// Act
var response = await HttpClient.GetAsync(BaseEndpoint);

// Assert
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<Result<List<TodoDto>>>();
Assert.NotNull(result);
Assert.NotNull(result.Data);
Assert.True(result.Data.Count >= minLengthResult);
}

[Fact]
public async Task DeleteAll_ShouldReturn_True()
{
// Act
var response = await HttpClient.DeleteAsync(BaseEndpoint);

// Assert
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<Result<bool>>();
Assert.NotNull(result);
Assert.True(result.Data);
}
}
C#

public class TodoTests : BaseIntegrationTest
{
private readonly List<TodoEntity> _todoEntitiesSeed =
[
new() { Title = "Test Todo 1", Description = "Description 1" },
new() { Title = "Test Todo 2", Description = "Description 2" },
new() { Title = "Test Todo 3", Description = "Description 3" },
];

private const string BaseEndpoint = "/api/todo";


...


[Fact]
public async Task GetAll_ShouldReturn_AllEntities()
{
// Arrange
var minLengthResult = _todoEntitiesSeed.Count;

// Act
var response = await HttpClient.GetAsync(BaseEndpoint);

// Assert
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<Result<List<TodoDto>>>();
Assert.NotNull(result);
Assert.NotNull(result.Data);
Assert.True(result.Data.Count >= minLengthResult);
}

[Fact]
public async Task DeleteAll_ShouldReturn_True()
{
// Act
var response = await HttpClient.DeleteAsync(BaseEndpoint);

// Assert
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<Result<bool>>();
Assert.NotNull(result);
Assert.True(result.Data);
}
}
In this case, I seeded the database with three entities. So in the GetAll I want to ensure that there will be at least 3 entities (at least because I have the CreateTodo test as well, which will lead it to 4, but then I have the delete by id which will lead again to 3, and then the delete all which would lead to 0).. I'm actually confused on how I should handle that without using an order to be honest
dreadfullydistinct
There are a couple of approaches you can use Either 1. Use collections to ensure the tests don’t run in parallel and have a common setup and reset method, using perhaps IAsyncLifetime on the base class Or 2. If your system is multi tenant, have each test run under its own user id or organisation id or whatever, so that they don’t interfere with each other, even when running in parallel I’m sure you can hack XUnit to get a deterministic test run order but don’t do that
Raso
RasoOP2w ago
@dreadfullydistinct thanks for the answer! Looks like I'll have to go with collections then.. right now it's not yet an application, it's literally as simple as the code above, I was just building a custom personal clean architecture template, and was adding some tests to that template.. I'm just testing simple CRUD, that's why I don't understand how I am already out of that xunit pattern.. I'll try to organize in collection, but it'll be ugly to my eyes 😂 like I'll have maybe three collections: - one for get by id, get all and update - one for delete by id - ine for delete all In this way I should be sure that all the tests run as they are intended to run, without risking to edit the database
dreadfullydistinct
With that suggested collection layout, xunit may still run the delete tests in parallel with the get which will be problematic
dreadfullydistinct
xUnit.net
Running Tests in Parallel
Documentation site for the xUnit.net unit testing framework
dreadfullydistinct
When I've done this before, we had a base test class which put everything in the same collection. That class implemented the IAsyncLifetime interface which reset the database and all that or apparently you can do [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
dreadfullydistinct
https://github.com/xunit/xunit/issues/980#issuecomment-248213473 xunit runs tests in a random order by design, to prevent exactly what you want - dependencies between tests
GitHub
Test Unit Execution Order and Grouping · Issue #980 · xunit/xunit
It would be most excellent if we were able to order the execution of the facts or theories in a file of some kind on Linux. Preferably with some UI items for configuration under VS Code. Additional...
Want results from more Discord servers?
Add your server