noeldemartin/laravel-dusk-mocking

在 Laravel Dusk 测试中模拟门面。

v6.5.2 2021-03-15 19:47 UTC

This package is auto-updated.

Last update: 2024-09-16 03:11:12 UTC


README

当使用 Laravel Dusk 运行浏览器测试时,无法模拟门面,如通常在 http 测试中进行的那样。此包旨在提供该功能。然而,它是通过一些权宜之计来实现的。建议在使用之前阅读免责声明(特别是限制)。

在将其添加到您的项目之前,您还可以尝试在此处使用一个简单的 Laravel 应用程序进行测试,该应用程序在 CI 环境中运行测试:laravel-dusk-mocking-sandbox

安装

使用 composer 安装

composer require --dev noeldemartin/laravel-dusk-mocking

将以下代码添加到您的基测试用例中(通常是 DuskTestCase)。

use NoelDeMartin\LaravelDusk\Browser;

...

protected function newBrowser($driver)
{
    return new Browser($driver);
}

用法

概念用法与 Laravel 文档中关于 模拟 的说明相同。唯一的区别是,在 Dusk 中,模拟可以独立于每个浏览器实例设置。因此,我们将调用实例方法而不是静态方法。以下是如何模拟 Mail 门面的示例:

public function testOrderShipping()
{
    $this->browse(function ($browser) use ($user) {
        $mail = $browser->fake(Mail::class);

        $browser->visit('...')
                // Perform order shipping...
                ->assertSee('Order purchased! Check your email for details!');

        $mail->assertSent(OrderShipped::class, function ($mail) use ($order) {
            return $mail->order->id === $order->id;
        });

        // Assert a message was sent to the given users...
        $mail->assertSent(OrderShipped::class, function ($mail) use ($user) {
            return $mail->hasTo($user->email) &&
                   $mail->hasCc('...') &&
                   $mail->hasBcc('...');
        });

        // Assert a mailable was sent twice...
        $mail->assertSent(OrderShipped::class, 2);

        // Assert a mailable was not sent...
        $mail->assertNotSent(AnotherMailable::class);
    });
}

注意,API 与 Http 测试相同。

配置

驱动程序通过请求序列化模拟数据,默认情况下使用 cookie。缺点是使用 cookie,存储信息的大小有限制(4KB)。因此,可以配置不同的驱动程序。在您的应用程序配置文件夹中创建一个名为 dusk-mocking.php 的文件来更改默认驱动程序。

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Driver
    |--------------------------------------------------------------------------
    |
    | This option controls the default driver for storing mock data.
    |
    | Supported: "cookies", "session"
    |
    */
    'driver' => 'session',

];

脚本超时

如以下所解释,每个测试可能会向应用程序发送多个请求以序列化和反序列化模拟。为了防止改变浏览器的状态,这是通过使用 JavaScript XHR 请求来完成的。默认情况下,这些请求将在 1 秒后超时,但可以使用 $javascriptRequestsTimeout 变量进行配置。

protected function newBrowser($driver)
{
    Browser::$javascriptRequestsTimeout = 5;

    return new Browser($driver);
}

扩展

这样做,只有调用 fake 的类似功能可用,而没有模拟方法。为了模拟自定义门面或修改 Laravel 的默认模拟,可以通过使用自定义模拟来扩展系统。这些模拟可以通过两种方式注册

  • 全局注册(使用模拟的每个测试都将使用它们)。将以下内容添加到您的基测试用例中(通常是 DuskTestCase):
public function setUp()
{
    parent::setUp();

    // Register fake mocks
    Mocking::registerFake(Mail::class, MyMailFake::class);
}
  • 另一个选项是仅为一个浏览器注册它们。如果需要为多个浏览器或出于美学原因(性能不受影响)使用不同的模拟,这可能很有用。
$this->browse(function (Browser $browser) {
    $browser->registerFake(Mail::class, MyMailFake::class);

    $mail = $browser->mock(Mail::class);

    // Proceed with the test
});

要了解如何实现这些模拟类,您可以查看Laravel 模拟 的实现,因为这些是默认使用的模拟。这些类也可以用作基类,通过扩展它们并添加任何修改来使用。

免责声明

大多数场景应该使用http测试来覆盖,因为它们在测试代码时既更快又更可靠。当测试前端和JavaScript时,也存在多个针对该领域的框架。然而,使用Dusk进行集成和端到端测试确实有一些价值。对于这些场景,很少需要模拟任何门面。但如果你发现自己处于这种情况,这个包可以帮到你😃。

它是如何工作的?

无法像正常http测试那样进行模拟的原因是,当Dusk运行测试时,它实际上是通过浏览器(例如在chromedriver上运行)发起实际请求。服务器和客户端不共享相同的运行时环境,这就是为什么无法让代码在它们之间进行通信。了解这一点后,这个包是如何工作的呢?嗯,当使用身份验证时,Dusk已经在某种程度上实现了类似的功能。创建了一些特殊的路由(作为惯例,以_dusk开头),用于与服务器进程进行通信,并且状态像正常Laravel会话一样持久化。鉴于这一点和其他用途,可以为测试配置不同的驱动程序(在您的.env.duskphpunit.dusk.xml文件中)。通过使用单独的会话驱动程序,例如测试数据库,可以在每个测试前后将其清除,以确保真正的黑盒场景。

简而言之,在底层发生的事情是,在每次请求开始时使用swap方法替换门面,并在结束时进行序列化。当从测试代码中调用断言方法时,数据将反序列化到测试运行时,并按常规执行断言。

您可以通过查看MockingProxyDriverMockingServiceProvider类来了解更多关于它是如何工作的。

限制

使用php的serializeunserialize函数实现了模拟服务的序列化和反序列化。由此产生的限制之一是闭包无法序列化。如果您看到错误信息“不允许序列化'Closure'”,这意味着在您模拟的服务中某个地方有一个闭包。

您可以通过几种方法来解决这个问题。

如果您可以控制该服务,您可以使用SerializableClosure类使您的闭包可序列化(Laravel核心已经在多个地方做了这样)。

如果您正在尝试模拟您无法控制第三方服务,您可以自己实现一个服务模拟而不是使用内置类。这个库已经这样做了,针对一些常见的Laravel服务,但它没有涵盖完整的Laravel API。您可以使用扩展功能注册您的自定义模拟。尽管这可能看起来是一项艰巨的任务,但请记住,您只需要为使用此包进行测试的功能实现模拟。并且,正如我们在本节开头讨论的那样,这不应该很多。