Laravel Dusk 控制台是一款 Laravel 扩展包,能够为你的 Dusk 测试套件提供漂亮的可视面板。通过它,你可以可视化运行 Dusk 测试时涉及的各个步骤,以及查看每个步骤的 DOM 快照。这对于调试浏览器测试、并搞清楚后台做了什么十分有用。同时,你还可以使用浏览器的调试工具来检查 DOM 快照。
<img src=”https://user-gold-cdn.xitu.io…;h=728&f=png&s=505665″ class=”rm-style”>
除了可视面板,此扩展包还提供了 Laravel Dusk 测试监视器。在你对 Dusk 测试进行修改后,便会自动执行测试过程。
该扩展包受到 Javascript 前端测试框架 —— Cypress 的强烈启发。
查看本扩展包,请移步 GitHub。
什么是 Laravel Dusk?
Laravel Dusk 提供了富有表现力的、易于使用的浏览器自动化和测试 API。使用 Laravel Dusk 编写测试用例,像在真正的浏览器上一样。比如,当你想在网站上测试拖放功能时,想要测试 Vue 组件或其他与 Javascript 相关功能,那么你无法使用 Laravels HTTP 测试 API 本身进行测试。
我认为 Laravel Dusk 是一个非常棒的软件包并且可以简化浏览器测试。以下是一个用户注册的示例测试,以便你可以了解 Laravel Dusk 的功能:
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 以及如何开始使用自己的浏览器测试的更多信息,请查看 官方文档。
使用 Laravel Dusk 控制台
在介绍 Laravel Dusk 控制台内部如何运行之前,让我们先瞄一眼如何在 Laravel 应用内安装并使用这个扩展包。
如下步骤假定你已经按照 官方文档 成功地安装了 Laravel Dusk;或者甚至你已经写好了一些 Dusk 测试。
首先,使用 Composer 安装本扩展包。
composer require –dev beyondcode/dusk-dashboard
接下来,打开 Laravel Dusk 生成的 DuskTestCase.php。你可以在 tests 目录里找到这个文件。
请务必使用本扩展包的测试用例(Test case)作为基类,而不是 Laravel Dusk 的测试用例。稍后我再告诉你内部原理。
找到此行:
use Laravel\Dusk\TestCase as BaseTestCase;
使用如下内容替换:
use BeyondCode\DuskDashboard\Testing\TestCase as BaseTestCase;
搞定。
现在你可以使用如下命令启动 Laravel Dusk 控制台,并执行你的测试了。
php artisan dusk:dashboard
类似这样的界面便会展示在你的面前:
<img src=”https://user-gold-cdn.xitu.io…;h=728&f=png&s=287914″ class=”rm-style”>
开始测试
只需按下「Start Tests」按钮,即可运行 Laravel Dusk 测试,并观察到你的应用被测试时的输出,以及所发生的行为。
随后,你便会看到 Dusk 测试产生的各种事件出现在你的控制台上。
还有一种启动 Dusk 测试的方法是,只要编辑任意一个测试文件然后保存即可。Laravel Dusk 控制台内置了文件监视器。
调试测试步骤
你可以通过点击展示在列表中的测试行为,来调试和检查它们。点击后,你将会看到 DOM 快照,表示当此行为被记录时的 HTML 页面状态。若此行为以某种方式操作过 DOM,那么你也可以点击「Before」和「After」按钮在事件发生「之前」或「之后」的 DOM 快照之间进行切换。
如下,一个按下「Register」按钮的小例子:
检查 XHR 请求
有时候,查看运行测试时发生的有关 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 也能正常运行。
UI 界面
最后,值得注意的是,此扩展包在它的面板界面里也有很多说道。它由 TailwindCSS 和 Vue 驱动来展示到来的事件以及过滤它们等等。你可以在这 这 查看起始页面的代码。
差不多就这些了。
更多翻译文章请见 PHP / Laravel 开发者社区 https://laravel-china.org/top…