I've been getting non-deterministic test results and decided to delve deeper into the issue. I've ended up on two tests behaving differently based on whether I Debug them, or just use Run. I've managed to magically fix one by deleting and renaming it.
I'm testing my API endpoint for updating usernames via WebApplicationFactory
(I'm using Mediator and a RavenDb that I recreate between every test if that makes a difference). The problem test case is User_Can_Not_Update_Username_If_It_Is_Already_Taken()
. I'm expecting a Conflict response, but most of the time I'm getting OK. The weirdest thing is that the response varies on where I put my breakpoint:
OK
Conflict.
I've tried clearing Rider cache and looking into my FakeApp
not disposing correctly. Trying to Debug before the UpdateUsernameCommandHandler
results in the Conflict
response status code, so I'm really at loss right now.
Here is the code of the test case:
[Fact]
public async Task User_Can_Not_Update_Username_If_It_Is_Already_Taken()
{
// Arrange
using var app = new FakeApp(DatabaseFixture.TestDbName);
var registerUserCommand = new RegisterUserCommand
{
Email = "oleksandr.torianyk@gmail.com"
};
var registerUserStringPayload = JsonConvert.SerializeObject(registerUserCommand);
var registerUserHttpContent = new StringContent(registerUserStringPayload, Encoding.UTF8, "application/json");
var registerUserResponse = await app.Client.PostAsync("/user", registerUserHttpContent);
var initialUpdateUsernameCommand = new UpdateUsernameCommand
{
Id = new Guid(await registerUserResponse.Content.ReadAsStringAsync()),
Username = "All-ToR"
};
var initialUpdateUsernameStringPayload = JsonConvert.SerializeObject(initialUpdateUsernameCommand);
var initialUpdateUsernameHttpContent = new StringContent(initialUpdateUsernameStringPayload, Encoding.UTF8, "application/json");
await app.Client.PutAsync("/user/username", initialUpdateUsernameHttpContent);
var updateUsernameCommand = new UpdateUsernameCommand
{
Id = new Guid(await registerUserResponse.Content.ReadAsStringAsync()),
Username = "All-ToR"
};
var updateUsernameStringPayload = JsonConvert.SerializeObject(updateUsernameCommand);
var updateUsernameHttpContent = new StringContent(updateUsernameStringPayload, Encoding.UTF8, "application/json");
// Act
var response = await app.Client.PutAsync("/user/username", updateUsernameHttpContent);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Conflict);
}
FakeApp
public class FakeApp : IDisposable
{
private readonly WebApplicationFactory<Startup> _appFactory;
public HttpClient Client { get; }
public FakeApp(string ravenDbName = default)
{
_appFactory = new WebApplicationFactory<Startup>().WithWebHostBuilder(webHostBuilder =>
webHostBuilder.ConfigureServices(services =>
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.Development.json", true)
.AddEnvironmentVariables()
.Build();
services.AddRavenDb(configuration, ravenDbName);
services.AddDependencies();
services.AddMediatR(Assembly.GetExecutingAssembly());
}));
Client = _appFactory.CreateClient();
}
public void Dispose()
{
Client.Dispose();
_appFactory.Dispose();
}
}