examples
Receive Email in PHP with MailSlurp (PHPUnit + Real Inboxes)
Learn how to send and receive real emails in PHP tests using MailSlurp. Create inboxes, wait for delivery, and assert content reliably in PHPUnit.
If you need reliable email assertions in PHP tests, treat email as an external system with deterministic checks.
This guide shows a practical pattern:
- create test inboxes with the API
- send an email between those inboxes
- wait for delivery
- assert subject, body, sender, recipient, and extracted tokens
Why API-based inbox testing instead of local SMTP only
Local SMTP traps are useful for basic development, but API-driven inboxes are usually better for CI and integration tests because you can:
- create isolated inboxes per test run
- wait for specific messages instead of sleeping
- parse and assert dynamic content (codes, links, IDs)
- run the same flow in local, CI, and shared environments
Prerequisites
- PHP 8+
- Composer
- PHPUnit
- a MailSlurp account and API key from app.mailslurp.com
Set your API key as an environment variable:
export MAILSLURP_API_KEY="your-api-key"
Install dependencies
composer require --dev mailslurp/mailslurp-client-php phpunit/phpunit
End-to-end PHPUnit example
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PHPUnit\Framework\TestCase;
final class EmailFlowTest extends TestCase
{
private function config(): MailSlurp\Configuration
{
$apiKey = getenv('MAILSLURP_API_KEY');
if (!$apiKey) {
throw new RuntimeException('MAILSLURP_API_KEY is not set');
}
return MailSlurp\Configuration::getDefaultConfiguration()
->setApiKey('x-api-key', $apiKey);
}
public function test_can_send_and_receive_email_between_two_inboxes(): void
{
$inboxApi = new MailSlurp\Apis\InboxControllerApi(null, $this->config());
$waitApi = new MailSlurp\Apis\WaitForControllerApi(null, $this->config());
$sender = $inboxApi->createInbox();
$recipient = $inboxApi->createInbox();
$sendOptions = new MailSlurp\Models\SendEmailOptions();
$sendOptions->setTo([$recipient->getEmailAddress()]);
$sendOptions->setSubject('Verify your account');
$sendOptions->setBody('Your verification code is ABC-123');
$inboxApi->sendEmail($sender->getId(), $sendOptions);
$email = $waitApi->waitForLatestEmail(
$recipient->getId(),
30000, // timeout ms
true // unreadOnly
);
$this->assertEquals($sender->getEmailAddress(), $email->getFrom());
$this->assertEquals($recipient->getEmailAddress(), $email->getTo()[0]);
$this->assertEquals('Verify your account', $email->getSubject());
$this->assertStringContainsString('verification code', $email->getBody());
preg_match('/code is ([A-Z]{3}-[0-9]{3})/', $email->getBody(), $matches);
$this->assertEquals('ABC-123', $matches[1]);
}
}
Pattern for Laravel projects
For Laravel, keep transport and assertions separate:
- keep your app sending through your normal mail channel
- use MailSlurp in tests to observe what users would actually receive
- assert business-critical payloads (links, codes, locale strings, legal footer)
This avoids test-only mail internals leaking into production code.
Hardening checklist for CI
- create fresh inboxes per test to avoid cross-test contamination
- use explicit wait conditions instead of
sleep()calls - keep message assertions focused on business outcomes
- mask secrets and rotate API keys regularly