Integration tests for ASP.NET Core APIs — WebApplicationFactory, xUnit, ConfigureTestServices, FluentAssertions, database isolation
97
96%
Does it follow best practices?
Impact
99%
1.45xAverage score across 5 eval scenarios
Passed
No known issues
Parcel's platform sends a welcome email every time a new user registers via its ASP.NET Core API. The registration endpoint calls an IEmailService that in production connects to SendGrid. The QA team wants to add integration tests for the registration flow, but the tests cannot call the real SendGrid API — it would spam inboxes, incur costs, and fail in CI where no API key is present.
The team needs integration tests that verify the registration endpoint works correctly end-to-end, but with the email service swapped for a controllable fake. Additionally, the fake should record which emails were "sent" so tests can verify the right email was dispatched as part of a successful registration.
Produce the following files:
tests/UserApi.Tests/FakeEmailService.cs — a fake implementation of IEmailService that records sent emails in memorytests/UserApi.Tests/TestFactory.cs — a WebApplicationFactory that replaces the real email service with the faketests/UserApi.Tests/RegistrationTests.cs — integration tests for the user registration endpointThe grader will inspect the source code of these files.
The following files are provided as inputs. Extract them before beginning.
=============== FILE: inputs/src/UserApi/Program.cs =============== using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext<UserDb>(opt => opt.UseInMemoryDatabase("UserDb")); builder.Services.AddSingleton<IEmailService, SendGridEmailService>();
var app = builder.Build();
app.MapPost("/api/users/register", async (RegisterInput input, UserDb db, IEmailService email) => { if (string.IsNullOrWhiteSpace(input.Email)) return Results.BadRequest(new { error = new { message = "Email is required" } });
if (await db.Users.AnyAsync(u => u.Email == input.Email))
return Results.Conflict(new { error = new { message = "Email already registered" } });
var user = new AppUser { Email = input.Email, Name = input.Name };
db.Users.Add(user);
await db.SaveChangesAsync();
await email.SendWelcomeAsync(user.Email, user.Name);
return Results.Created($"/api/users/{user.Id}", new { data = new { user.Id, user.Email, user.Name } });});
app.MapGet("/api/users/{id:int}", async (int id, UserDb db) => { var user = await db.Users.FindAsync(id); return user is null ? Results.NotFound(new { error = new { message = "User not found" } }) : Results.Ok(new { data = new { user.Id, user.Email, user.Name } }); });
app.Run();
=============== FILE: inputs/src/UserApi/IEmailService.cs =============== public interface IEmailService { Task SendWelcomeAsync(string toEmail, string name); }
=============== FILE: inputs/src/UserApi/SendGridEmailService.cs =============== public class SendGridEmailService : IEmailService { public async Task SendWelcomeAsync(string toEmail, string name) { // Production implementation: calls SendGrid API // Requires SENDGRID_API_KEY environment variable await Task.Delay(100); // simulate network call Console.WriteLine($"[SendGrid] Sent welcome to {toEmail}"); } }
=============== FILE: inputs/src/UserApi/Models.cs =============== public class AppUser { public int Id { get; set; } public string Email { get; set; } = ""; public string Name { get; set; } = ""; }
public record RegisterInput(string Email, string Name);
=============== FILE: inputs/src/UserApi/UserDb.cs =============== using Microsoft.EntityFrameworkCore;
public class UserDb : DbContext { public UserDb(DbContextOptions<UserDb> options) : base(options) { } public DbSet<AppUser> Users => Set<AppUser>(); }