When using Mail::Fake() in a test and then calling a function which uses the render() function of the Mailable class the following error is produced
Error: Call to undefined method Illuminate\Support\Testing\Fakes\MailFake::render()
Mailable->render() is a function which was only added in laravel 5.6
on inspection the MailFake class (which does not have a render() method
This appears to be a simple oversight.
Currently use of the asserts provided by mail::fake() is impossible if the function being tested uses the render() method.
If you're rendering the mailable why exactly are you using a fake? Can you provide us with some code?
I am using a fake so that I can test that code which sends an email works without actually sending an email.
The email content is not returned by a function at any point so the only way to test it would be to use an assert function which operates on a mock.
The mail::fake() mock has the Mail::assertSent and Mail::assertNotSent methods which are clearly intended for this purpose.
here is the relevant test code (shortened to only include what is relevant), names have been changed for privacy so code may be a little hard to read.
public function testCustomerIsAlertedBeforeLicenceExpires()
{
//Prevent any real mails being sent (cant currently use this due to possible laravel bug)
Mail::fake();
//Populate test DB with test data.
//Within the required range (will trigger a reminder email)
factory(App\ExpireingLicences::class)->create([
'notice_sent' => 0,
'date_last_reminded' => null,
'expiry_date' => self::$twoWeeksFromNow
]);
//ubove the required range (will not trigger a reminder email)
factory(App\EsetRenewal::class)->create([
'notice_sent' => 0,
'date_last_reminded' => null,
'expiry_date' => self::$fourWeeksFromNow
]);
//bellow the required range (will not trigger a reminder email)
factory(App\EsetRenewal::class)->create([
'notice_sent' => 0,
'date_last_reminded' => null,
'expiry_date' => self::$oneWeeksFromNow
]);
//Run the test.
$this->artisan('sendReminder');
//Assert that DB values are as they should be.
$this->assertDatabaseHas('ExpireingLicences', [
'notice_sent' => 1
]);
//Assert that Mail was sent. (cant currently use this due to apparent laravel bug)
Mail::assertSent(renewalNotice::class, 1);
}
Sent from my Android phone with mail.com Mail. Please excuse my brevity.On 30/04/2018, 16:54 "Till Krüss" notifications@github.com wrote:
Closed #24005.
—You are receiving this because you authored the thread.Reply to this email directly, view it on GitHub, or mute the thread.
{"api_version":"1.0","publisher":{"api_key":"05dde50f1d1a384dd78767c55493e4bb","name":"GitHub"},"entity":{"external_key":"github/laravel/framework","title":"laravel/framework","subtitle":"GitHub repository","main_image_url":"https://cloud.githubusercontent.com/assets/143418/17495839/a5054eac-5d88-11e6-95fc-7290892c7bb5.png","avatar_image_url":"https://cloud.githubusercontent.com/assets/143418/15842166/7c72db34-2c0b-11e6-9aed-b52498112777.png","action":{"name":"Open in GitHub","url":"https://github.com/laravel/framework"}},"updates":{"snippets":[{"icon":"DESCRIPTION","message":"Closed #24005."}],"action":{"name":"View Issue","url":"https://github.com/laravel/framework/issues/24005#event-1601822396"}}}
I would still be asking the question as the first by @mateusjatenee. I can't really imagine a situation when you would need to render a mailable in a console command. Would you be able to add the code in the actual command as well?
If you fake the mail component you can't render it, you'll need to stop faking it and just set the mail driver to log so you can render if you want.
I have this same issue. I'm rendering a mailable in my controller to save it to a database column but I'm also sending out an email.
$client_email = new ClientEmail;
$client_email->subject = $subject;
$client_email->message = $message;
$client_email->email_data = (new SendClientEmail($client, $subject, $message, $user, null))->render();
$client_email->user()->associate($user['id']);
$client_email->client()->associate($client['id']);
$client_email->save();
Mail::to($client['clients_email'])->send(
new SendClientEmail($client, $subject, $message, $user, $client_email->id)
);
My tests are throwing this error:
Call to undefined method Illuminate\Support\Testing\Fakes\MailFake::render()
Here is the test:
public function authUserCanSendEmailToClient()
{
$this->withoutExceptionHandling();
$user = $this->authProUser();
$client = factory(Client::class)->create();
$this->assertDatabaseHas('clients', $client->toArray());
$session = factory(Session::class)->create([
'user_id' => $user->id,
'client_id' => $client->id
]);
$this->assertDatabaseHas('sessions', $session->toArray());
$data = [
'client' => $client,
'session' => $session,
'template' => [
'subject' => $this->faker->sentence,
'message' => $this->faker->sentence
]
];
Mail::fake();
$this->json('PUT', route('send_client_email.update'), $data)
->assertOk();
Mail::assertSent(
SendClientEmail::class,
function ($mail) use ($client, $data) {
return $mail->hasTo($client->clients_email);
}
);
Mail::assertSent(SendClientEmail::class, 1);
}
https://laravel.com/docs/6.x/mocking#mail-fake recommends using Mail::fake() during tests. But it's not possible to get the final html of a Mailable during unit testing. I want to do something like this but can't:
public function testSomething()
{
Mail::fake();
// Do some stuff.
Mail::assertSent(
ForgotDetailsMail::class,
function (ForgotDetailsMail $mail) use ($user) {
$html = $mail->render(); // Fails with "Call to undefined method Illuminate\Support\Testing\Fakes\MailFake::render()"
return stripos($html, 'some text') !== false;
}
);
}
I always write two kind of test for mails:
Mail::fake and can affect multiple tests, depending where it was _generated_To me this also makes sense: I don't need to check the rendering in all cases I send an email. When I trigger a send I want to know: receiver, data passed correct (subject, view data); and this is all possible.
But I only need one (or a few) _dedicated_ tests for the rendering, which are usually very primitive:
$mail = new Mail\Whatever($yourParams, …);
$html = $mail->render();
// your assertions
@mfn
I like that approach as a solution. I hadn't checked but had assumed that render() would never work in a test. But it appears you can create a separate test to test the output of render() as long as you don't have the Mail::fake call.
But it appears you can create a separate test to test the output of
render()as long as you don't have theMail::fakecall.
Exactly 👍
Most helpful comment
I always write two kind of test for mails:
Mail::fakeand can affect multiple tests, depending where it was _generated_To me this also makes sense: I don't need to check the rendering in all cases I send an email. When I trigger a send I want to know: receiver, data passed correct (subject, view data); and this is all possible.
But I only need one (or a few) _dedicated_ tests for the rendering, which are usually very primitive: