not sure if you can check if the email was send with phpunit / testing but you could use https://mailtrap.io/ to check your test emails.
Testing if email was sent with out sending it
Im writing an acceptance test to check if the built in "reset password" functionality is working properly. How do I test if the mail is being triggered and sent without sending it.
/** @test */
public function a_user_can_reset_its_password()
{
$user = factory(App\User::class)->create(['email'=> 'john@faker.com']);
$this->visit('/password/reset')
->type('john@faker.com','email')
->press('submit__email')
->see('We have e-mailed your password reset link!');
}
open your .env file and change the Mail_Driver entry from smtp to log: MAIL_DRIVER=log
now you can inspect the result in your log file located at /storage/logs/laravel.log
And of course, if you want to fully automate it and not need to look in the log files, you can inject a spy for the Mailer class and check that it receives the correct mail sending call.
This new video by @adamwathan covers this very topic.
I find that you are not able to view the mail content if you simply use the Log mail driver. Update your EventServiceProvider to capture the mail sending event and log the actual mail content.
public function boot(DispatcherContract $events)
{
$events->listen('mailer.sending', function ($message) {
Log::info($message);
});
}
There are a few ways to test mail sending without actually sending mails and without changing the application configuration.
1. Mocking the Mail facade
It is described in the Laravel documentation.
use Illuminate\Support\Facades\Mail;
Mail::fake();
// Some app business here
Mail::assertSent(OrderShipped::class, function ($mail) {
return $mail->hasTo('[email protected]');
});
It is not a very good way to test because it catches only mails sent using the Mail facade (doesn't intercept mails sent through a notification or using a mailer retrieved via dependency injection).
2. Mocking the application mailer instance
In this way all mails are intercepted because the core mailer instance is substituted.
$mailer = new \Illuminate\Support\Testing\Fakes\MailFake();
$this->app->instance('mailer', $mailer);
// Some app business here
$mailer->assertSent(OrderShipped::class, function ($mail) {
return $mail->hasTo('[email protected]');
});
The assertions work the same way as in the facade mocking way. Unfortunately MailFake can't do any assertions with mails sent by the notification system. You can use Mockery instead of MailFake to inspect them:
$mailer = \Mockery::mock(\Illuminate\Contracts\Mail\Mailer::class);
$this->app->instance('mailer', $mailer);
$mailer->shouldReceive('send')->once()->withArgs(function (...$args) {
// Your test here. The arguments structure differs case-to-case, so it is not a trivial stuff.
});
// Some app business here
\Mockery::close(); // Actually you don't need to call it in a Laravel test because the framework already does it
3. Mocking the mailer underlying Swift Mailer instance
This way is the most complicated but it covers all the mail sending cases and provides a single message format for testing. It also requires Mockery.
$mailer = $this->app->make('mailer');
$swiftMailer = \Mockery::mock($mailer->getSwiftMailer());
$mailer->setSwiftMailer($swiftMailer);
$swiftMailer->shouldReceive('send')->once()->withArgs(function (\Swift_Message $mail) {
return array_key_exists('[email protected]', $mail->getTo());
});
// Some app business here
\Mockery::close(); // Actually you don't need to call it in a Laravel test because the framework already does it
4. Using the array driver
PHPUnit can change environment variables during testing, so we can change the Mail driver only for the tests and use the built-in array driver to catch the messages. This approach is simple and covers all the mail sending cases.
Make sure that your application takes the mail driver from the environment variables in config/mail.php. Example of an appropriate configuration:
// ...
'driver' => env('MAIL_DRIVER', 'smtp'),
// ...
Add the environment variable to the PHPUnit configuration (the phpunit.xml file):
<phpunit ...>
...
<php>
...
<env name="MAIL_DRIVER" value="array"/>
</php>
</phpunit>
And get the «sent» messages in your tests:
// Some app business here
$emails = $this->app->make('swift.transport')->driver()->messages();
$this->assertCount($emails, 1);
$this->assertEquals(['[email protected]'], array_keys($emails[0]->getTo()));
This is actually the best answer.
Just found this after some needing to ditch Mailtrap for its ridiculous new API limits. Excellent post, I was tearing my hair out trying to find a sane and simple way to test email. Thanks.
Why don't you use MAILHOG? You can test mails locally even without internet and very easy to install and use in Laravel.
In .env
MAIL_MAILER=smtp
MAIL_HOST=0.0.0.0
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
[email protected]
MAIL_FROM_NAME="${APP_NAME}"
Lol. I forgot that I was answering an old post. But anyways will keep it so that someone might find it useful.
Interesting how nobody seems to actually read the flippin' manual before posting in the forums. 🙄
Right, or maybe... just maybe, not everyone is on the last version of Laravel and don't have options that are available since just recent updated versions. For example, if you are on 5.2
Please or to participate in this conversation.