简体   繁体   English

Laravel 单元测试邮件

[英]Laravel unit testing emails

My system sends a couple of important emails.我的系统发送了几封重要的电子邮件。 What is the best way to unit test that?单元测试的最佳方法是什么?

I see you can put it in pretend mode and it goes in the log.我看到您可以将其置于假装模式并记录在日志中。 Is there something to check that?有什么可以检查的吗?

There are two options.有两种选择。

Option 1 - Mock the mail facade to test the mail is being sent.选项 1 - 模拟邮件外观以测试正在发送的邮件。 Something like this would work:像这样的事情会起作用:

$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());
    });

Option 2 is much easier - it is to test the actual SMTP using MailCatcher.me .选项 2 更容易 - 它是使用MailCatcher.me测试实际的 SMTP。 Basically you can send SMTP emails, and 'test' the email that is actually sent.基本上,您可以发送 SMTP 电子邮件,并“测试”实际发送的电子邮件。 Laracasts has a great lesson on how to use it as part of your Laravel testing here . Laracasts 有一个关于如何在 Laravel 测试中使用它的重要课程

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

"Option 1" from "@The Shift Exchange" is not working in Laravel 5.1, so here is modified version using Proxied Partial Mock: “@The Shift Exchange”中的“Option 1”在 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();

If you just don't want the e-mails be really send, you can turn off them using the "Mail::pretend(true)"如果您只是不想真正发送电子邮件,则可以使用“Mail::pretend(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
    }
}

If any one is using docker as there development environment I end up solving this by:如果有人使用 docker 作为开发环境,我最终会通过以下方式解决这个问题:

Setup设置

.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),

(I had a conflict with this environment variable for some reason) (由于某种原因,我与这个环境变量发生了冲突)

Carrying on...进行...

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>

Testing测试

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');
    }
}

(This has been copied and edited from my own code so might not work first time) (这是从我自己的代码中复制和编辑的,所以第一次可能不起作用)

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

I think that inspecting the log is not the good way to go.我认为检查日志不是好方法。

You may want to take a look at how you can mock the Mail facade and check that it receives a call with some parameters.您可能想看看如何模拟 Mail 外观并检查它是否收到带有一些参数的调用。

if you are using Notifcations in laravel you can do that like below如果你在 laravel 中使用通知,你可以像下面那样做

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

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

If you want to test everything around the email, use如果您想测试电子邮件周围的所有内容,请使用

Mail::fake()

But if you want to test your Illuminate\\Mail\\Mailable and the blade , then follow this example.但是如果你想测试你的Illuminate\\Mail\\Mailableblade ,那么按照这个例子。 Say, you want to test a Reminder email about some payment, where the email text should have product called 'valorant' and some price in 'USD'.假设,您想测试有关某些付款的提醒电子邮件,其中电子邮件文本应包含名为“valorant”的产品和一些以“美元”表示的价格。

 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);
}

The important part here is ->render() - that is how you make Illuminate\\Mail\\Mailable to run build() function and process the blade .这里的重要部分是->render() - 这就是你如何让Illuminate\\Mail\\Mailable运行build()函数并处理blade

Another importan thing is auth()->logout();另一个重要的事情是auth()->logout(); - because normally emails being processed in a queue that run in a background environment. - 因为通常在后台环境中运行的队列中处理电子邮件。 This environment has no user and has no request with no URL and no IP...这个环境没有用户,没有没有URL和IP的请求......

So you must be sure that you are rendering the email in your unit test in a similar environment as in production.因此,您必须确保在与生产环境类似的环境中,在单元测试中呈现电子邮件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM