Laravel单元测试电子邮件

时间:2014-08-09 20:08:17

标签: php laravel laravel-4

我的系统发送了几封重要的电子邮件。单元测试的最佳方法是什么?

我看到你可以把它放在假装模式下,它会进入日志。有什么可以检查的吗?

8 个答案:

答案 0 :(得分:10)

有两种选择。

选项1 - 模拟邮件外观以测试邮件是否正在发送。像这样的东西会起作用:

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

选项2更容易 - 它是使用MailCatcher.me测试实际的SMTP。基本上,您可以发送SMTP电子邮件,并“测试”实际发送的电子邮件。 Laracasts has a great lesson on how to use it as part of your Laravel testing here

答案 1 :(得分:4)

"选项1"来自" @The Shift Exchange"在Laravel 5.1中不起作用,所以这里是使用Proxied Partial Mock的修改版本:

$mock = \Mockery::mock($this->app['mailer']->getSwiftMailer());
$this->app['mailer']->setSwiftMailer($mock);
$mock
    ->shouldReceive('send')
    ->withArgs([\Mockery::on(function($message)
    {
        $this->assertEquals('My subject', $message->getSubject());
        $this->assertSame(['foo@bar.com' => null], $message->getTo());
        $this->assertContains('Some string', $message->getBody());
        return true;
    }), \Mockery::any()])
    ->once();

答案 2 :(得分:3)

对于Laravel 5.4检查Mail::fake()https://laravel.com/docs/5.4/mocking#mail-fake

答案 3 :(得分:2)

如果您不想真正发送电子邮件,可以使用“Mail ::假装(true)”关闭它们

class TestCase extends Illuminate\Foundation\Testing\TestCase {
    private function prepareForTests() {
      // e-mail will look like will be send but it is just pretending
      Mail::pretend(true);
      // if you want to test the routes
      Route::enableFilters();
    }
}

class MyTest extends TestCase {
    public function testEmail() {
      // be happy
    }
}

答案 4 :(得分:0)

我认为检查日志不是一个好方法。

您可能需要了解如何模拟Mail外观并检查它是否接收到带有某些参数的调用。

答案 5 :(得分:0)

如果有人在使用docker作为开发环境,我最终通过以下方式解决此问题:

设置

.env

...
MAIL_FROM       = noreply@example.com

MAIL_DRIVER     = smtp
MAIL_HOST       = mail
EMAIL_PORT      = 1025
MAIL_URL_PORT   = 1080
MAIL_USERNAME   = null
MAIL_PASSWORD   = null
MAIL_ENCRYPTION = null

config/mail.php

# update ...

'port' => env('MAIL_PORT', 587),

# to ...

'port' => env('EMAIL_PORT', 587),

(由于某种原因,我与此环境变量发生冲突)

继续...

docker-compose.ymal

mail:
    image: schickling/mailcatcher
    ports:
        - 1080:1080

app/Http/Controllers/SomeController.php

use App\Mail\SomeMail;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;


class SomeController extends BaseController
{
    ...
    public function getSomething(Request $request)
    {
        ...
        Mail::to('someone@example.com')->send(new SomeMail('Body of the email'));
        ...
    }

app/Mail/SomeMail.php

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class SomeMail extends Mailable
{
    use Queueable, SerializesModels;

    public $body;

    public function __construct($body = 'Default message')
    {
        $this->body = $body;
    }

    public function build()
    {
        return $this
            ->from(ENV('MAIL_FROM'))
            ->subject('Some Subject')
            ->view('mail.someMail');
    }
}

resources/views/mail/SomeMail.blade.php

<h1>{{ $body }}</h1>

测试

tests\Feature\EmailTest.php

use Tests\TestCase;
use Illuminate\Http\Request;
use App\Http\Controllers\SomeController;

class EmailTest extends TestCase
{
    privete $someController;
    private $requestMock;

    public function setUp()
    {
        $this->someController = new SomeController();
        $this->requestMock = \Mockery::mock(Request::class);
    }

    public function testEmailGetsSentSuccess()
    {
        $this->deleteAllEmailMessages();

        $emails = app()->make('swift.transport')->driver()->messages();
        $this->assertEmpty($emails);

        $response = $this->someController->getSomething($this->requestMock);

        $emails = app()->make('swift.transport')->driver()->messages();
        $this->assertNotEmpty($emails);

        $this->assertContains('Some Subject', $emails[0]->getSubject());
        $this->assertEquals('someone@example.com', array_keys($emails[0]->getTo())[0]);
    }

    ...

    private function deleteAllEmailMessages()
    {
        $mailcatcher = new Client(['base_uri' => config('mailtester.url')]);
        $mailcatcher->delete('/messages');
    }
}

(这是从我自己的代码复制并编辑的,因此可能第一次无法使用)

(来源:https://stackoverflow.com/a/52177526/563247

答案 6 :(得分:0)

如果您在laravel中使用Notifcations,则可以像下面这样

Notification::fake();
$this->post(...);
$user = User::first();
Notification::assertSentTo([$user], VerifyEmail::class);

https://laravel.com/docs/7.x/mocking#notification-fake

答案 7 :(得分:0)

如果要测试电子邮件周围的所有内容,请使用

Mail::fake()

但是,如果您要测试Illuminate\Mail\Mailableblade,请遵循以下示例。假设您想测试有关某笔付款的提醒电子邮件,该电子邮件中的文本应包含商品“ valorant”和某些“ USD”价格。

 public function test_PaymentReminder(): void
{
    /* @var $payment SalePayment */
    $payment = factory(SalePayment::class)->create();
    auth()->logout();

    $paymentReminder = new PaymentReminder($payment);
    $html            = $paymentReminder->render();

    $this->assertTrue(strpos($html, 'valorant') !== false);
    $this->assertTrue(strpos($html, 'USD') !== false);
}

这里的重要部分是->render()-这是使Illuminate\Mail\Mailable运行build()函数并处理blade的方式。

另一个重要的事情是auth()->logout();-因为通常在后台环境中运行的队列中正在处理电子邮件。该环境没有用户,也没有URL,IP……的请求...

因此,您必须确保要在与生产环境类似的环境中在单元测试中呈现电子邮件。