noeldemartin / laravel-dusk-mocking
在 Laravel Dusk 测试中模拟门面。
Requires
- php: >=7.2.0
- laravel/dusk: >=5.4.0
- laravel/framework: >=6.0.0
Requires (Dev)
- fakerphp/faker: ^1.13
- mockery/mockery: >=1.2.3
- phpunit/phpunit: >=8.3.0
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.dusk
或phpunit.dusk.xml
文件中)。通过使用单独的会话驱动程序,例如测试数据库,可以在每个测试前后将其清除,以确保真正的黑盒场景。
简而言之,在底层发生的事情是,在每次请求开始时使用swap
方法替换门面,并在结束时进行序列化。当从测试代码中调用断言方法时,数据将反序列化到测试运行时,并按常规执行断言。
您可以通过查看MockingProxy、Driver和MockingServiceProvider类来了解更多关于它是如何工作的。
限制
使用php的serialize
和unserialize
函数实现了模拟服务的序列化和反序列化。由此产生的限制之一是闭包无法序列化。如果您看到错误信息“不允许序列化'Closure'”,这意味着在您模拟的服务中某个地方有一个闭包。
您可以通过几种方法来解决这个问题。
如果您可以控制该服务,您可以使用SerializableClosure类使您的闭包可序列化(Laravel核心已经在多个地方做了这样)。
如果您正在尝试模拟您无法控制第三方服务,您可以自己实现一个服务模拟而不是使用内置类。这个库已经这样做了,针对一些常见的Laravel服务,但它没有涵盖完整的Laravel API。您可以使用扩展功能注册您的自定义模拟。尽管这可能看起来是一项艰巨的任务,但请记住,您只需要为使用此包进行测试的功能实现模拟。并且,正如我们在本节开头讨论的那样,这不应该很多。