Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test Mail Facade in Laravel 4

I cant seem to get the Mail Facade to accept a ->with() command for Testing.

This works:

Mail::shouldReceive('send')->once();

But this does not work:

Mail::shouldReceive('send')->with('emails.welcome')->once();

and neither does this:

Mail::shouldReceive('send')->with('emails.welcome', array(), function(){})->once();

and neither does this:

Mail::shouldReceive('send')->with('emails.welcome', array(), function($message){})->once();

All give the following error:

"No matching handler found for Illuminate\Mail\Mailer::send("emails.welcome", Array, Closure)"

So how can I test Mail to check what it is receiving?

Also - for bonus points - is it possible to test what Mail is doing inside the closure? i.e. can I check what $message->to() is set to?

edit: my mail code:

Mail::send("emails.welcome", $data, function($message)
{
    $message->to($data['email'], $data['name'])->subject('Welcome!');
});
like image 303
Laurence Avatar asked Aug 23 '13 15:08

Laurence


People also ask

How do I test my email in Laravel?

To send your test email, start the application and access/send-mail path in your browser. Shortly after, you should see the email in Sandbox -> Inboxes. Alternatively, you can integrate Email Sandbox into your Laravel app via an API. For more details, refer to the API documentation.

How do you mock a facade?

Unlike traditional static method calls, facades may be mocked. We can mock the call to the static facade method by using the shouldReceive method, which will return an instance of a Mockery mock.

What is assertStatus in Laravel?

} The get method makes a GET request into the application, while the assertStatus method asserts that the returned response should have the given HTTP status code. In addition to this simple assertion, Laravel also contains a variety of assertions for inspecting the response headers, content, JSON structure, and more.


1 Answers

The code examples below assumes PHP 5.4 or newer - if you're on 5.3 you'll need to add $self = $this before the following code and use ($self) on the first closure, and replace all references to $this inside the closure.

Mocking SwiftMailer

The simplest way is to mock the Swift_Mailer instance. You'll have to read up on what methods exist on the Swift_Message class in order to take full advantage of it.

$mock = Mockery::mock('Swift_Mailer');
$this->app['mailer']->setSwiftMailer($mock);
$mock->shouldReceive('send')->once()
    ->andReturnUsing(function(\Swift_Message $msg) {
        $this->assertEquals('My subject', $msg->getSubject());
        $this->assertEquals('[email protected]', $msg->getTo());
        $this->assertContains('Some string', $msg->getBody());
    });

Assertions on closures

Another way to solve this is to run assertions on the closure passed to Mail::send. This does not look all that clean, and its error messages can be rather cryptic, but it works, is very flexible, and the technique can be used for other things as well.

use Mockery as m;

Mail::shouldReceive('send')->once()
    ->with('view.name', m::on(function($data) {
        $this->assertContains('my variable', $data);
        return true;
    }), m::on(function($closure) {
        $message = m::mock('Illuminate\Mailer\Message');
        $message->shouldReceive('to')
            ->with('[email protected]')
            ->andReturn(m::self());
        $message->shouldReceive('subject')
            ->with('Email subject')
            ->andReturn(m::self());
        $closure($message);
        return true;
    }));

In this example, I'm running an assertion on the data passed to the view, and I'll get an error from Mockery if the recipient address, subject or view name is wrong.

Mockery::on() allows you to run a closure on a parameter of a mocked method. If it returns false, you'll get the "No matching handler found", but we want to run assertions so we just return true. Mockery::self() allows for chaining of methods.

If at any point you don't care what a certain parameter of a method call is, you can use Mockery::any() to tell Mockery that it accepts anything.

like image 89
Andreas Avatar answered Sep 21 '22 16:09

Andreas