Laravel Dusk 控制台是一款 Laravel 扩展包,可以为你的 Dusk 测试套件提供漂亮的可视面板。经过它,你能够可视化运行 Dusk 测试时涉及的各个步骤,以及查看每一个步骤的 DOM 快照。这对于调试浏览器测试、并搞清楚后台作了什么十分有用。同时,你还可使用浏览器的调试工具来检查 DOM 快照。php
除了可视面板,此扩展包还提供了 Laravel Dusk 测试监视器。在你对 Dusk 测试进行修改后,便会自动执行测试过程。html
该扩展包受到 Javascript 前端测试框架 —— Cypress 的强烈启发。前端
Laravel Dusk 提供了富有表现力的、易于使用的浏览器自动化和测试 API。使用 Laravel Dusk编写测试用例,像在真正的浏览器上同样。好比,当你想在网站上测试拖放功能时,想要测试Vue组件或其余与 Javascript 相关功能,那么你没法使用 Laravels HTTP 测试 API 自己进行测试。laravel
我认为 Laravel Dusk 是一个很是棒的软件包而且能够简化浏览器测试。 如下是一个用户注册的示例测试,以便你能够了解 Laravel Dusk 的功能:git
public function test_can_register()
{
$faker = Factory::create();
$this->browse(function($browser) use ($faker) {
$password = $faker->password(9);
$browser->visit('/register')
->assertSee('Register')
->type('name', $faker->name)
->type('email', $faker->safeEmail)
->type('password', $password)
->type('password_confirmation', $password)
->press('Register')
->assertPathIs('/home');
});
}
复制代码
要了解更多关于 Laravel Dusk 以及如何开始使用本身的浏览器测试的更多信息,请查看 官方文档。github
在介绍 Laravel Dusk 控制台内部如何运行以前,让咱们先瞄一眼如何在 Laravel 应用内安装并使用这个扩展包。web
以下步骤假定你已经按照 官方文档 成功地安装了 Laravel Dusk;或者甚至你已经写好了一些 Dusk 测试。chrome
首先,使用 Composer 安装本扩展包。浏览器
composer require --dev beyondcode/dusk-dashboard
复制代码
接下来,打开 Laravel Dusk 生成的 DuskTestCase.php
。你能够在 tests
目录里找到这个文件。bash
请务必使用本扩展包的测试用例(Test case
)做为基类,而不是 Laravel Dusk 的测试用例。稍后我再告诉你内部原理。
找到此行:
use Laravel\Dusk\TestCase as BaseTestCase;
复制代码
使用以下内容替换:
use BeyondCode\DuskDashboard\Testing\TestCase as BaseTestCase;
复制代码
搞定。
如今你可使用以下命令启动 Laravel Dusk 控制台,并执行你的测试了。
php artisan dusk:dashboard
复制代码
相似这样的界面便会展现在你的面前:
只需按下「Start Tests」按钮,便可运行 Laravel Dusk 测试,并观察到你的应用被测试时的输出,以及所发生的行为。
随后,你便会看到 Dusk 测试产生的各类事件出如今你的控制台上。
还有一种启动 Dusk 测试的方法是,只要编辑任意一个测试文件而后保存便可。Laravel Dusk 控制台内置了文件监视器。
你能够经过点击展现在列表中的测试行为,来调试和检查它们。点击后,你将会看到 DOM 快照,表示当此行为被记录时的 HTML 页面状态。若此行为以某种方式操做过 DOM,那么你也能够点击 「Before」和「After」按钮在事件发生「以前」或「以后」的 DOM 快照之间进行切换。
以下,一个按下「Register」按钮的小例子:
有时候,查看运行测试时发生的有关 XHR 请求的其余信息可能会颇有用。例如:你网站上又一个按钮,它将对某个服务端执行 GET 请求。
Dusk Dashboard 容许您记录 XHR 事件,并显示响应状态和响应路径。
默认状况下 XHR 请求检查不会启用,由于它须要你修改浏览器功能。
要启用 XHR 的请求记录,打开你的 DuskTestCase.php
,在文件里,有个 driver
方法,用于设置不一样测试操做的 WebDriver。因为此程序包须要对此驱动程序的功能进行一些调整,所以须要使用 $this->enableNetworkLogging
方法调用来封装 DesiredCapabilities
对象。
protected function driver()
{
$options = (new ChromeOptions)->addArguments([
'--disable-gpu',
'--headless',
'--window-size=1920,1080',
]);
return RemoteWebDriver::create(
'http://localhost:9515', $this->enableNetworkLogging(
DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY, $options
)
)
);
}
复制代码
经过添加此功能,该程序包将启用记录 XHR 请求和响应信息所需的功能。
基本思路十分简单:运行一个 WebSocket 服务,控制台用户链接到这个 WebSocket 服务,接着 PHPUnit 便会将浏览器事件和失败信息发送至全部 WebSocket 链接。
如下是具体的实现方式:
在内部,此扩展包向你的 Laravel 应用内添加了一个名为 StartDashboardCommand
的命令。当此命令被执行时,就会 启动 一个由 Ratchet 开发的 WebSocket 服务。最初我考虑基于我同 Freek 一块儿开发的 Laravel Websockets 实现此功能,然而随后就毙了这个想法。缘由很简单,此扩展包仅能用做开发依赖项,而且我不须要 Pusher 或 Laravel 广播功能,由于广播是经过 PHPUnit 内部实现的。
译者注:Freek 意指 Freek Van der Herten。 另,截至目前,此扩展包也已经发布 v1.0.x 稳定版本。
接下来,我添加两条路由到 WebSocket 服务。
$dashboardRoute = new Route('/dashboard', ['_controller' => new DashboardController()], [], [], null, [], ['GET']);
$this->app->routes->add('dashboard', $dashboardRoute);
$eventRoute = new Route('/events', ['_controller' => new EventController()], [], [], null, [], ['POST']);
$this->app->routes->add('events', $eventRoute);
复制代码
$dashboardRoute
是一条普通 HTTP 控制器路由,用于输出 Laravel Dusk 控制台的 HTML 视图。
就是这么简单,它只作一件事——返回 HTML 视图:
class DashboardController extends Controller
{
public function onOpen(ConnectionInterface $connection, RequestInterface $request = null)
{
$connection->send(
str(new Response(
200,
['Content-Type' => 'text/html'],
file_get_contents(__DIR__.'/../../../resources/views/index.html')
))
);
$connection->close();
}
}
复制代码
$eventRoute
一样是一个 HTTP 路由,但只容许 POST
请求。它被用来在 PHPUnit 和 WebSocket 客户端之间通信。
一样十分简单,也只作一件事——接收 POST 数据,并广播给全部已链接的 WebSocket 客户端:
class EventController extends Controller
{
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null)
{
try {
/*
* 以下即为从 PHPUnit 测试发来的 POST 数据,
* 发送到已链接的客户端。
*/
foreach (Socket::$connections as $connection) {
$connection->send($request->getBody());
}
$conn->send(str(new Response(200)));
} catch (Exception $e) {
$conn->send(str(new Response(500, [], $e->getMessage())));
}
$conn->close();
}
}
复制代码
这是整个扩展包最乏味的部分。由于若想收集全部 Laravel Dusk 方法,并将它们广播到 WebSocket 链接,那么必须代理全部的消息再收集它们。
在本扩展包自定义的 TestCase
类里,咱们可以重写(override
)浏览器实例被建立的过程。那么,此处就是我注入自定义的浏览器(Browser
)类的地方。它负责代理现有方法并收集全部行为,同时转发给 WebSocket 链接。
protected function newBrowser($driver)
{
return new Browser($driver);
}
复制代码
没什么高端操做。接下来,我本来想直接建立一个新类,传给它 Laravel Dusk 的浏览器类,随后使用 __call
魔术方法代理全部的方法。这可以省下一大堆代码,但也会引出两个问题:
用户没法使用 IDE 自动完成、方法提示功能。
对我来讲有点忍不了,我认为这是个很是重要的特性 —— 尤为是对于测试工具来讲。开发者并不了解 API 的输入和输出,所以须要 IDE 的提示。
另外一个问题是,我不只仅想在浏览器行为发生后记录 DOM 快照,在某些特定的行为发生前,一样想记录快照。
因此这就是我为什么不得不像下面这样,代理全部 Laravel Dusk 方法:
/** @inheritdoc */
public function assertTitle($title)
{
$this->actionCollector->collect(__FUNCTION__, func_get_args(), $this);
return parent::assertTitle($title);
}
复制代码
好了,这样我便能收集并记录各个行为,且依然维持着 IDE 自动完成功能。棒棒哒!
如今你能看到这里的 actionCollector
是 PHPUnit 和 WebSocket 客户端之间的桥梁。它收集得到的信息,并用例如测试名称和 WebSocket POST 推送的端点数据来丰富它:
protected function pushAction(string $name, array $payload)
{
try {
$this->client->post('http://127.0.0.1:'.StartDashboardCommand::PORT.'/events', [
RequestOptions::JSON => [
'channel' => 'dusk-dashboard',
'name' => $name,
'data' => $payload,
],
]);
} catch (\Exception $e) {
// Dusk-Dashboard 服务器多是关闭的。没必要惊慌。
}
}
复制代码
它由 try-catch 包裹来保证即便在 Dusk Dashboard 服务器关闭时 Laravel Dusk 也能正常运行。
最后,值得注意的是,此扩展包在它的面板界面里也有不少说道。它由 TailwindCSS 和 Vue 驱动来展现到来的事件以及过滤它们等等。你能够在这 这 查看起始页面的代码。
差很少就这些了。
更多翻译文章请见 PHP / Laravel 开发者社区 laravel-china.org/topics/2119…