Skip to content

.NET and xUnit

Terminal window
dotnet add package Mailcatchr.Sdk

Read the key and mailbox id from the environment or user secrets, never from source.

public sealed class MailFixture : IDisposable
{
public MailcatchrClient Client { get; } = new(new MailcatchrClientOptions
{
ApiKey = Environment.GetEnvironmentVariable("MAILCATCHR_API_KEY")
?? throw new InvalidOperationException("MAILCATCHR_API_KEY is not set"),
});
public string MailboxId { get; } = Environment.GetEnvironmentVariable("MAILCATCHR_MAILBOX_ID")!;
public static string UniqueAddress(string label) =>
$"{label}-{DateTimeOffset.UtcNow:yyyyMMddHHmmssfff}@acme-ci.mailcatchr.com";
public void Dispose() => Client.Dispose();
}
public sealed class PasswordResetTests(MailFixture mail) : IClassFixture<MailFixture>
{
[Fact]
public async Task Reset_email_contains_a_working_link()
{
var address = MailFixture.UniqueAddress("reset");
await App.RequestPasswordResetAsync(address);
var summary = await mail.Client.AwaitMessageAsync(mail.MailboxId,
to: address,
subject: "Reset your password",
timeout: TimeSpan.FromSeconds(30));
summary.Should().NotBeNull("the reset email should arrive within 30 s");
var message = await mail.Client.GetMessageAsync(mail.MailboxId, summary!.Id);
var link = Regex.Match(message.TextBody!, @"https://\S+/reset\?token=\S+").Value;
link.Should().NotBeEmpty();
var response = await App.HttpClient.GetAsync(link);
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
}

AwaitMessageAsync returns null on timeout rather than throwing, so the assertion message can say what was expected.

When the code under test is itself a mail client, read the mailbox with MailKit and per-mailbox credentials. See Mail clients.