如何在 Laravel 中使用 Cypress 来做 E2E 测试
Laravel 测试很棒! 大多数时候,我们使用 PHPUnit 进行测试,对吧? 编写单元/功能测试是确保我们的代码按预期执行,这样我们就可以放心地部署它们。 大多数时候,我们从开发人员的角度进行测试。 有时,我们会模拟外部资源(即外部 API)的行为,以便我们的测试独立于外部事物。 我们不希望我们的测试依赖于外部系统,这使得测试变得麻烦。
我们想的是,可以从用户的角度进行测试吗? 是的当然。 我们称之为端到端测试(或 E2E),我们来从头到尾模拟真实的用户场景。
端到端测试是一种从头到尾测试整个软件产品以确保应用程序流按预期运行的技术。 它定义了产品的系统依赖关系,并确保所有集成部件按预期协同工作。参考文献: What is End-to-End (E2E) Testing?
大多数时候,复杂的系统依赖于很多其他系统。通过 E2E 测试,我们希望确保一切都是集成的并且没有错误。 如果任何子系统出现任何故障,我们会立即知道!因此,编写单元/功能测试以及端到端测试是提高产品质量的好方法!
好的,我们知道什么是端到端测试。 下一个问题是,我们应该怎么做? E2E 测试框架有很多平台,例如 Cypress、WebdriverJS、Protractor、NightwatchJS 等。但现在我们将使用 Cypress。 将 Cypress 集成到我们现有的 Laravel 项目中非常容易,这就是我们在本文中使用 Cypress 的原因。 我们开始吧!
内容概览
- 我们的目标
- Laravel 身份验证脚手架
- Cypress 安装和设置
- 身份验证测试
1. 我们的目标
在开始之前,最好知道我们将要构建什么。因此,为了模拟 E2E 测试,我们将使用 Laravel Authentication 脚手架。 如果你不熟悉,身份验证脚手架是一项功能,你将立即拥有身份验证登录/注册页面及其功能。 然后,我们将开始编写测试来模拟用户场景,在他/她已经登录时注册、登录和检查用户会话。
2. Laravel 身份验证脚手架
首先,让我们创建 Laravel 项目。
composer create-project laravel/laravel --prefer-dist laravel-cypress
我们需要身份验证脚手架。 让我们用这个命令安装它。
composer require laravel/ui
安装脚手架
php artisan ui vue --authnpm install && npm run dev
完成了!如果你运行服务,你会注意到页面右上角有登录和注册按钮。
3. Cypress 安装和设置
好的,是时候将 Cypress 安装到我们的项目中了。 使用 npm 安装库。
npm install cypress --save-dev
然后,打开 Cypress,输入:
./node_modules/.bin/cypress open
如果在这些步骤之前出现任何问题,那么您可能需要先安装 Cypress:
node_modules/.bin/cypress.cmd install — force
如果你打开了 Cypress,你应该会看到一个新的 Cypress 窗口出现。
随意通过单击其中一项测试来探索示例测试 :)
4. 身份验证测试
在我们开始创建测试类之前,让我们讨论一些事情。如果你已经熟悉 Laravel 中的 PHPUnit 测试,相信你已经了解工厂。如果你不熟悉它,我有一个快速的解释。工厂是一个函数,你可以在其中创建「假」数据模型并将其存储在数据库中。
当你想创建一个虚拟用户或其他模型并在测试中使用它时,工厂很有用。 想象一下,当你想创建一个用户可以购买 3 本书的场景,那么你可能需要一个工厂在运行测试之前创建这 3 本书,对吧?
有时,Cypress 测试也需要工厂,是的,我们现在需要它。所以第一件事是,让我们创建一个辅助路由/函数,该作业正在创建虚拟数据/工厂。 我们需要另一个帮助路径来使用户通过身份验证。
在 routes 中创建一个新文件,将其命名为 _testing.php。
<?php
Route::get('/login', function () {
Auth::login(\App\Models\User::factory()->create());
});
Route::get('/create', function () {
$modelClass = 'App\Models\\' . request('model');
return $modelClass::factory()->create();
});
然后,我们需要将这个 _testing 路由导入我们的网络路由。但是,这个测试路线只在我们做测试的时候使用,对吧?我们不想在生产环境中公开这条路线。让我们先创建一个中间件,然后在我们的 web 路由中导入测试路由。
php artisan make:middleware TestingRoutes
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
class TestingRoutes
{
/**
* 处理请求
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if (App::environment('production')) abort (404);
return $next($request);
}
}
非常简单的中间件。 如果应用程序在生产环境中运行,那么我们会使用 404 状态码中止它。
然后在 app\Http\Kernel.php 中,添加我们刚刚创建的中间件。
/**
* 应用程序的路由中间件。
*
* 这些中间件可以分配给组或单独使用。
*
* @var array
*/
protected $routeMiddleware = [
...
'testing' => \App\Http\Middleware\TestingRoutes::class
];
最后一件事,将我们的 _testing 路由导入 web.php 路由文件。
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])
->middleware(['auth'])
->name('home');
Auth::routes();
Route::prefix('_testing')
->middleware(['testing'])
->group(__DIR__ . '/_testing.php');
太棒了! 我们现在有测试路由。让我们继续执行 Cypress!
在创建测试之前,我们需要在 Cypress 中的 Cypress/support/commands.js 文件中创建一些助手函数。
/**
* Login helper function.
*/
Cypress.Commands.add('login', () => cy.request('/_testing/login'));
/**
* Create model factory:
* ex: baseUrl/_testing/create?model=User
*/
Cypress.Commands.add('create', model => {
return cy.request('/_testing/create?model=' + model).its('body');
});
所以我们要做的是创建一些辅助命令来执行登录和创建功能。我们可以在测试类的任何地方调用这个命令。
现在是我们创建测试的时候了。创建一个新文件夹,我们在 Cypress/integration 文件夹中将其命名为 laravel-cypress。 并在文件夹中创建一个新文件 authentication.spec.js。
describe('Authenticaation', () => {
before(() => {
// 在代码块的所有测试之前运行一次
})
beforeEach(() => {
// 在代码块的每个测试之前运行
cy.visit('/home')
})
afterEach(() => {
// 在代码块的每个测试之后运行
})
after(() => {
// 在代码块的所有测试之后运行一次
})
it('will redirect to login page when visitors accessing home', () => {
cy.visit('/home')
cy.url().should('contains', '/login')
})
it('will register a user', () => {
cy.visit('/register')
cy.get('input[name=name]').type('admin')
cy.get('input[name=email]').type('user' + new Date().valueOf() + '@mail.com')
cy.get('input[name=password]').type('password')
cy.get('input[name=password_confirmation]').type('password')
cy.get('button').contains('Register').click()
cy.url().should('contain', '/home')
})
it('will log in a user', () => {
cy.create('User').then(user => {
cy.visit('/login');
cy.get('input[name="email"]').type(user.email);
cy.get('input[name="password"]').type('password');
cy.get('button[type="submit"]').click();
cy.contains(user.name);
cy.contains('You are logged in!');
});
})
it('maintains user session', () => {
cy.login();
cy.visit('/home');
cy.contains('You are logged in!');
})
})
在第 3-18 行,我添加了一些空白函数。在我们的示例中它没有做任何事情,只是想展示 Cypress 具有专门运行的功能。
然后在第 21-24 行,我们断言访客不应访问 /home 端点。你可能会猜到,cy.visit() 函数是我们访问 URL 的方式。
在第 27-37 行,我们尝试填写电子邮件、密码等的输入表单。cy.get() 功能是通过选择器或 别名 获取一个或多个 DOM 元素。
还记得我们创建的命令吗? 这些命令用于第 41 和 54 行。
一切都准备好了,是时候测试 Cypress 了。 使用此命令打开 Cypress。
./node_modules/.bin/cypress open
你应该会看到一个新的 Cypress 窗口打开,然后单击我们刚刚创建的身份验证测试。
就是这样! 这就是我们在 Laravel 项目中使用 Cypress 创建端到端测试的方式。我喜欢 Cypress 的地方在于,Cypress 拥有完整的文档,其中包含简单的函数命名。 当然,在 Cypress 中还有很多需要探索的地方,但至少我们已经知道如何将 Cypress 应用到我们的项目中。 感谢阅读,下次见。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。