Laravel Notification Mailable无法在测试中工作

时间:2019-09-18 12:53:48

标签: php laravel testing

我正在使用通知,以便在触发通知时向所有用户发送邮件。

这在手动“浏览网站”用法(将POST提交给ContactController)中很好用,但是在我的测试中,邮件外观声称没有发送邮件。

控制器:

<?php

namespace App\Http\Controllers;

use App\Notifications\ContactRequestNotification;
use App\User;
use Illuminate\Http\Request;
use Notification;

class ContactController extends Controller
{
    public function create(Request $request)
    {
        $validateData = $request->validate([
            'name' => 'required',
            'email' => 'required|email',
            'message' => 'required'
        ]);

        Notification::send(User::all(), new ContactRequestNotification($validateData));

        return $validateData;
    }
}

通知:

<?php

namespace App\Notifications;

use App\Mail\ContactMail;
use Illuminate\Mail\Mailable;
use Illuminate\Notifications\Notification;

class ContactRequestNotification extends Notification
{
    /**
     * @var array
     */
    private $contactData;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct($contactData)
    {
        $this->contactData = $contactData;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param mixed $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param mixed $notifiable
     * @return Mailable
     */
    public function toMail($notifiable)
    {
        return (new ContactMail($this->contactData['name'], $this->contactData['email'], $this->contactData['message']))->to($notifiable->email);
    }

    /**
     * Get the array representation of the notification.
     *
     * @param mixed $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return $this->contactData;
    }
}

可邮寄:

<?php

namespace App\Mail;

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

class ContactMail extends Mailable
{
    use Queueable, SerializesModels;

    /** @var string */
    public $name;

    /** @var string */
    public $email;

    /** @var string */
    public $message;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($name, $email, $message)
    {
        $this->name = $name;
        $this->email = $email;
        $this->message = $message;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->markdown('emails.contact')
            ->from(env('MAIL_FROM'))
            ->subject('New contact request');
    }
}

测试:

<?php

namespace Tests\Feature;

use App\Mail\ContactMail;
use App\Notifications\ContactRequestNotification;
use App\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Support\Facades\Mail;
use Notification;
use Tests\TestCase;

class ContactFormTest extends TestCase
{
    use DatabaseMigrations;

    /** @test */
    public function a_notification_gets_created_when_the_contact_form_is_used()
    {
        Notification::fake();

        $user = factory(User::class)->create();

        $response = $this->post('/contact', [
            'name' => 'John Doe',
            'email' => 'email@email.com',
            'message' => 'This is a test message'
        ]);

        $response->assertStatus(200);

        Notification::assertSentTo($user, ContactRequestNotification::class);
    }

    /** @test */
    public function a_mail_is_send_when_the_contact_form_is_used()
    {
        $this->withExceptionHandling();

        factory(User::class)->create();

        Mail::fake();

        Mail::assertNothingSent();

        $data = [
            'name' => 'John Doe',
            'email' => 'email@email.com',
            'message' => 'This is a test message'
        ];

        $response = $this->post('/contact', $data);

        $response->assertStatus(200);

        Mail::assertSent(ContactMail::class, 1);
    }
}

“ a_notification_gets_created_when_the_contact_form_is_used”测试运行没有问题,但是第二个测试用例导致:

/usr/bin/php /home/vendor/phpunit/phpunit/phpunit --configuration /home/phpunit.xml --filter "/(::a_mail_is_send_when_the_contact_form_is_used)( .*)?$/" Tests\Feature\ContactFormTest /home/tests/Feature/ContactFormTest.php --teamcity
PHPUnit 8.3.4 by Sebastian Bergmann and contributors.


The expected [App\Mail\ContactMail] mailable was sent 0 times instead of 1 times.
Failed asserting that false is true.

在浏览器中执行相同任务时,会将邮件发送给用户。

关于我在这里缺少什么的任何提示?互联网搜索显示其他一些人有此问题,但都没有找到解决方案(或者我无法执行正确的搜索)

谢谢!

1 个答案:

答案 0 :(得分:1)

发送通知电子邮件的MailChannel驱动程序似乎没有使用Mail外观,这意味着Mail::fake不会对其产生影响。相反,它直接在send上调用Mailable方法,然后在send(邮件驱动程序)上调用Mailer

您可以将Mailable实例替换为MailFake的实例(Mail::fake使用的实例),但是看起来MailFake不能满足$view是一个数组(MailChannel传递给Mailable的情况)。

幸运的是,Laravel源代码包含一个示例,说明了他们如何测试在SendingMailNotificationsTest中发送邮件的通知。他们模拟MailerMarkdown实例,并检查传递的参数。您可以执行类似的操作:

use Mockery as m;
use Illuminate\Contracts\Mail\Mailable;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Mail\Markdown;
use Illuminate\Mail\Message;

class ContactFormTest extends TestCase
{

    protected function tearDown(): void
    {
        parent::tearDown();
        m::close();
    }

    protected function setUp(): void
    {
        parent::setUp();

        $this->mailer = m::mock(Mailer::class);
        $this->markdown = m::mock(Markdown::class);
        $this->instance(Mailer::class, $this->mailer);
        $this->instance(Mailer::class, $this->markdown);
    }

    public function a_mail_is_send_when_the_contact_form_is_used()
    {
        $this->withExceptionHandling();

        $user = factory(User::class)->create();

        $this->markdown->shouldReceive('render')->once()->andReturn('htmlContent');

        $this->markdown->shouldReceive('renderText')->once()->andReturn('textContent');

        $data = [
            'name' => 'John Doe',
            'email' => 'email@email.com',
            'message' => 'This is a test message'
        ];

        $notification = new ContactRequestNotification($data);

        $this->mailer->shouldReceive('send')->once()->with(
            ['html' => 'htmlContent', 'text' => 'textContent'],
            array_merge($notification->toMail($user)->toArray(), [
                '__laravel_notification' => get_class($notification),
                '__laravel_notification_queued' => false,
            ]),
            m::on(function ($closure) {
                $message = m::mock(Message::class);
                $message->shouldReceive('to')->once()->with([$user->email]);
                $closure($message);
                return true;
            })
        );


        $response = $this->post('/contact', $data);

        $response->assertStatus(200);

    }
}

就我个人而言,我现在只想对toMail类的ContactRequestNotification方法进行单元测试,因为我认为上面的方法不是很漂亮。