Carnou

Testing the object you pass in a mocked method

January 13, 2021

Let’s image we have a class:

class EmailSender
{
    public function send(Email $email): void
    {
        // your implementation
    }
}

The Email class will hold the details to the email you want to send:

class Email
{
    public string $template;

    public string $email;
    
    //...
}

If you want to mock the EmailSender you can do something like this:

...

class EmailSenderTest extends TestCase
{
    public function testEmailSender()
    {
        $this->instance(
            EmailSender::class,
            Mockery::mock(EmailSender::class, function (MockInterface $mock) {
                $mock->shouldReceive('send')
                    ->withAnyArgs()
                    ->once();
            })
        );
        
        ...
    }
}

The approach above you are testing if the send message is called once, but you haven`t tested if the argument provided to the method has the correct data on it.

To add checks on the object as well, you can assert it in the with for a mocked object, as:

...

class EmailSenderTest extends TestCase
{
    public function testEmailSender()
    {
        $this->instance(
            EmailSender::class,
            Mockery::mock(EmailSender::class, function (MockInterface $mock) {
                $mock->shouldReceive('send')
                    ->with(Mockery::on(function (Email $email) {
                        $this->assertEquals('my-template', $email->template);
                        $this->assertEquals('[email protected]', $email->email);
                    })->once();
            })
        );
        
        ...
    }
}

This way your test is only going to pass if the Email class has expected template and email properties.


Mauro Baptista

Always trying to go a little bit further!