簡體   English   中英

Laravel 單元測試郵件

[英]Laravel unit testing emails

我的系統發送了幾封重要的電子郵件。 單元測試的最佳方法是什么?

我看到您可以將其置於假裝模式並記錄在日志中。 有什么可以檢查的嗎?

有兩種選擇。

選項 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 有一個關於如何在 Laravel 測試中使用它的重要課程

對於 Laravel 5.4 檢查Mail::fake()https ://laravel.com/docs/5.4/mocking#mail-fake

“@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();

如果您只是不想真正發送電子郵件,則可以使用“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
    }
}

如果有人使用 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

我認為檢查日志不是好方法。

您可能想看看如何模擬 Mail 外觀並檢查它是否收到帶有一些參數的調用。

如果你在 laravel 中使用通知,你可以像下面那樣做

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

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

如果您想測試電子郵件周圍的所有內容,請使用

Mail::fake()

但是如果你想測試你的Illuminate\\Mail\\Mailableblade ,那么按照這個例子。 假設,您想測試有關某些付款的提醒電子郵件,其中電子郵件文本應包含名為“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);
}

這里的重要部分是->render() - 這就是你如何讓Illuminate\\Mail\\Mailable運行build()函數並處理blade

另一個重要的事情是auth()->logout(); - 因為通常在后台環境中運行的隊列中處理電子郵件。 這個環境沒有用戶,沒有沒有URL和IP的請求......

因此,您必須確保在與生產環境類似的環境中,在單元測試中呈現電子郵件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM