Mails

Don't actually send emails in tests

Use Mail::fake() to assert emails are queued without sending them.

Mailable
// app/Mail/OrderConfirmation.php
class OrderConfirmation extends Mailable
{
    public function __construct(public Order $order) {}

    public function envelope(): Envelope
    {
        return new Envelope(
            from: new Address('[email protected]', 'Example Shop'),
            subject: "Order #{$this->order->id} Confirmed",
        );
    }

    public function content(): Content
    {
        return new Content(
            markdown: 'emails.order-confirmation',
            with: [
                'trackingUrl' => "https://example.com/track/{$this->order->tracking_number}",
            ],
        );
    }
}
Controller
// app/Http/Controllers/OrderController.php
public function store(StoreOrderRequest $request)
{
    $order = Order::create($request->validated());

    Mail::to($order->email)
        ->send(new OrderConfirmation($order));
    // ...
}
Feature Test — assert mail is sent
// tests/Feature/Http/Controllers/OrderController/StoreTest.php
Mail::fake();

$this->post(route('orders.store'), $data);

Mail::assertOutgoingCount(1);

Mail::assertQueued(OrderConfirmation::class, function ($mail) {
    $this->assertTrue($mail->hasTo('[email protected]'));
    $this->assertSame('Order #1 Confirmed', $mail->subject);

    return true;
});
Unit Test — assert mail content
// tests/Unit/Mail/OrderConfirmationTest.php
class OrderConfirmationTest extends TestCase
{
    #[Test]
    public function mail_has_correct_subject(): void
    {
        $order = Order::factory()->create();
        $mail = new OrderConfirmation($order);

        $this->assertSame(
            "Order #{$order->id} Confirmed",
            $mail->envelope()->subject
        );
    }

    #[Test]
    public function mail_has_correct_recipient(): void
    {
        $order = Order::factory()->create([
            'email' => '[email protected]',
        ]);
        $mail = new OrderConfirmation($order);

        $this->assertTrue($mail->hasTo('[email protected]'));
    }

    #[Test]
    public function mail_has_tracking_url_in_data(): void
    {
        $order = Order::factory()->create([
            'tracking_number' => 'TRACK123',
        ]);
        $mail = new OrderConfirmation($order);

        $this->assertArrayHasKey('trackingUrl', $mail->content()->with);
        $this->assertStringContainsString(
            'TRACK123',
            $mail->content()->with['trackingUrl']
        );
    }
}