Log inStart creating →
DEVELOPERS · 10 min read

Wiring Real Test Email Accounts Into a CI/CD Pipeline

When a mock inbox is not enough: provisioning real provider accounts for CI, pooling versus ephemeral, cleanup, and keeping the suite deterministic.

MailMaker EditorialProduct guide · Published 2026-09-25 · Updated 2026-09-25
KEY TAKEAWAYS

What you will leave with

  • Use real provider accounts only where the mock genuinely cannot answer the question
  • A managed pool beats per-run creation for almost every suite
  • Account cleanup belongs in the pipeline, not in someone's calendar
  • Never let account credentials reach build logs or artifacts

Decide where a mock stops being enough

Most email assertions in a test suite should not touch a real provider. If you are checking that your application queued a message, rendered a template, or called the right service, a capture server answers that faster and more reliably than a real inbox ever will.

Real accounts earn their place when the thing under test is the provider's behaviour rather than yours. Deliverability differences, provider-specific link handling, OAuth sign-in flows, and rendering in an actual client are the cases where a mock gives you a confident green build that tells you nothing.

  • Mock: your app sent the right message
  • Mock: template rendering and content assertions
  • Real: provider-specific link and redirect handling
  • Real: sign-in flows tied to a provider identity
  • Real: rendering and placement in an actual client

Pool the accounts; do not create one per run

Creating a fresh account inside every pipeline run is the intuitive design and almost always the wrong one. It makes your build depend on a third-party signup succeeding within the build timeout, it multiplies cost by your commit frequency, and it turns a flaky provider into a red build on an unrelated change.

A pool decouples those. Provision a set of accounts ahead of time, check one out at the start of a run, reset its state, and return it at the end. The pipeline's dependency becomes your own pool service, which you control, rather than a provider's signup flow, which you do not.

Refill the pool out of band

Run pool replenishment as its own scheduled job rather than as part of any test run. It submits a MailMaker task when the pool drops below a threshold, stores the task ID, and polls until the batch reaches a terminal state. If the batch finishes partially, the job adds what completed and alerts rather than blocking.

This is the pattern that keeps CI fast. Your test run never waits on account creation, and a provider issue shows up as a pool warning instead of as a failing build on someone's pull request.

  • Trigger on a low-water mark, not on a test run
  • Store the task ID with the replenishment job
  • Accept partial completion and alert on the gap
  • Keep a buffer sized to your peak concurrent runs

Make cleanup part of the pipeline

Test accounts accumulate. Without an automatic retirement path, a pool that started as twenty accounts becomes four hundred stale ones over a year, each holding whatever your tests happened to send it. That is both a security problem and a cost nobody budgeted.

Give every pooled account an expiry, and have the same job that refills the pool also retire whatever is past it. The cost of building this is a few hours; the cost of not building it is discovered much later and by someone else.

Keep credentials out of the build record

Account credentials should reach the test process through your existing secret mechanism and nothing else. The common leak is not the secret store; it is a debug print, a failed-assertion dump, or a screenshot artifact that happens to include a logged-in session.

Mask account fields in your test framework's failure output, exclude artifacts from any run that touched a real account unless you have reviewed what they contain, and scope the MailMaker API key used by CI to that purpose alone so it can be rotated without touching anything else.

  • Inject credentials through the secret store only
  • Mask account fields in assertion output
  • Review screenshot and video artifacts before retention
  • Use a dedicated, rotatable API key for CI
COMMON QUESTIONS

Questions about this workflow

Should every test get a fresh email account?

Rarely. A checked-out pooled account with reset state gives you the same isolation without making your build depend on a third-party signup completing in time.

How large should the pool be?

Size it to peak concurrent runs plus a buffer for accounts in cooldown or retirement. Most teams over-estimate; start small and measure checkout contention.

What happens if the replenishment batch fails partially?

The task reports successful and failed counts separately, and failed items cost nothing. Add what completed to the pool and let the alert decide whether to retry.

Can I use test-mode API keys for this?

Use a separate key scoped to CI regardless of mode, so it can be rotated independently of keys used by production systems.

PUT IT INTO PRACTICE

Run the workflow in MailMaker.

Start in the dashboard with your provider selected, or connect the same task lifecycle to your application through the API.