我的系统发送了几封重要的电子邮件。 单元测试的最佳方法是什么?
我看到你可以将其置于假装模式,它会记录在日志中。有什么可以检查的吗?
有两种选择。
选项 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('[email protected]', $msg->getTo());
$this->assertContains('Some string', $msg->getBody());
});
选项 2 更容易 - 它是使用 MailCatcher.me 测试实际的 SMTP。基本上,您可以发送 SMTP 电子邮件,并“测试”“实际”发送的电子邮件。 Laracasts 在此处提供了有关如何使用它作为 Laravel 测试一部分的精彩课程。
$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(['[email protected]' => null], $message->getTo());
$this->assertContains('Some string', $message->getBody());
return true;
}), \Mockery::any()])
->once();
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
}
}
设置
.env
...
MAIL_FROM = [email protected]
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('[email protected]')->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('[email protected]', 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/563247Mail::fake()
但是如果您想测试您的
Illuminate\Mail\Mailable
和
blade
,请按照此示例进行操作。假设您想测试有关某些付款的提醒电子邮件,其中电子邮件文本应包含名为“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...
因此,您必须确保在单元测试中在与生产环境类似的环境中渲染电子邮件。
您可能想看看如何模拟 Mail 外观并检查它是否收到带有某些参数的调用。
我的建议是使用像
Msgdrop这样的 API,您可以在测试中使用 RESTful API 创建临时/一次性(真实)电子邮件地址,然后在测试中使用该地址。 示例:
curl https://api.msgdrop.io/v1/addresses \
-H 'Authorization: Bearer {YOUR_API_KEY}' \
--data ''
然后您可以通过另一个 RESTful API 调用从收件箱检索电子邮件:
curl https://api.msgdrop.io/v1/inbox/{CREATED_EMAIL_ADDRESS_ID}\
-H 'Authorization: Bearer {YOUR_API_KEY}'
响应将是一个大的 JSON 对象,其中包含有关电子邮件的所有信息,包括收件人、抄送、密件抄送、主题、HTML 和纯文本内容、附件、链接甚至垃圾邮件分数。
像这样的 API 驱动测试方法也比使用一些 UI 驱动测试更可靠,其中 UI 元素不断变化并破坏测试,而且恕我直言,它也比所有那些假 SMTP 服务器更好,因为您的软件可以使用信誉良好的电子邮件发送服务,例如 Mailgun 或SendGrid 并仍然以相同的方式测试电子邮件。您将无法将 SendGrid 的 SMTP 设置配置到假服务器。
查看非常广泛的文档:
https://docs.msgdrop.io