CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl-labs/aspnet-testing

Integration tests for ASP.NET Core APIs — WebApplicationFactory, xUnit, ConfigureTestServices, FluentAssertions, database isolation

97

1.45x
Quality

96%

Does it follow best practices?

Impact

99%

1.45x

Average score across 5 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

task.mdevals/scenario-5/

Test a Notification-Driven User Registration API

Problem/Feature Description

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.

Output Specification

Produce the following files:

  • tests/UserApi.Tests/FakeEmailService.cs — a fake implementation of IEmailService that records sent emails in memory
  • tests/UserApi.Tests/TestFactory.cs — a WebApplicationFactory that replaces the real email service with the fake
  • tests/UserApi.Tests/RegistrationTests.cs — integration tests for the user registration endpoint

The grader will inspect the source code of these files.

Input 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>(); }

evals

tile.json