编程

Laravel + Cypress 集成

927 2022-02-17 19:46:53

本扩展包提供了必要的模板文件,用于使用Cypress快速开始测试Laravel应用。

 

安装

如果你还未安装Cypress, 第一步便是如下:

npm install cypress --save-dev && npx cypress open

初始化安装时,Cypress会在项目根目录下创建一个./cypress目录, 以及cypress.json配置文件。

现在你可以通过composer安装相关依赖,以development-only的方式拉取相关依赖。

composer require laracasts/cypress --dev

最后运行cypress:boilerplate命令,为Cypress测试复制初始化模板文件。

php artisan cypress:boilerplate

好了,现在可以开始了。我们已经在项目cypress.json文件中声明了一些初始设置。查看该文件确保一切如期设置。特别注意,将baseUrl属性设置正确(默认为应用的APP_URL环境设置)。

环境处理

在运行php artisan cypress:boilerplate命令后,你的根目录下会添加一个.env.cypress文件。此文件是.env文件的副本。请根据实际测试需要更新相关项目。

你可能会使用特定的数据库,使你的Cypress验收测试与你的开发环境数据库隔离。

DB_CONNECTION=mysql
DB_DATABASE=cypress

当运行Cypress测试时,此包会自动备份你的主.env文件,切换到 .env.cypressw文件。等到测试完成,环境文件会被重置成原来的文件。

All Cypress tests run according to the environment specified in .env.cypress.

然而,当Cypress测试失败时,手动浏览导致测试失败的应用状态通常是有用的手段。如果你的环境在每次测试运行后都自动恢复,你无法手动浏览。解决办法是,暂时禁用Cypress重置环境的任务。在ypress/support/index.js中注释掉如下部分:

after(() => {
  // cy.task("activateLocalEnvFile", {}, { log: false });
});

这样就可以了!

API

这个扩展包会将一系列命令添加到Cypress工作流中,使之更加熟悉Laravel测试环境。

我们通过在应用中暴露一些Cypress特定的端口(endpoints)实现。不用担心,这些接口在生产环境中不会生效。

cy.login()

找一个已存在的用户匹配可选属性,将其设置会测试的授权用户。如果用户不存在,则新建用户然后登录。

test('authenticated users can see the dashboard', () => {
  cy.login({ username: 'JohnDoe' });

  cy.visit('/dashboard').contains('Welcome Back, JohnDoe!');
});

你需要在积极加载用户模型的关联模型或者在服务器返回前指定特定的模型工厂状态,在cy.login()中传入一个对象如下:

test('authenticated users can see the dashboard', () => {
    cy.login({
        attributes: { username: 'JohnDoe' },
        state: ['guest'],
        load: ['profile']
    });

    cy.visit('/dashboard').contains('Welcome Back, JohnDoe!');
});

如果用PHP编写,这个对象会被翻译成如下:

$user = User::factory()->guest()->create([ 'username' => 'JohnDoe' ])->load('profile');

auth()->login($user);

cy.currentUser()

从服务器中获取当前授权用户。相当于Laravel的auth()->user().

test('assert the current user has email', () => {
    cy.login({ email: 'joe@example.com' });

    cy.currentUser().its('email').should('eq', 'joe@example.com');
    
    // or...
    
    cy.currentUser().then(user => {
        expect(user.email).to.eql('joe@example.com');
    });
});

cy.logout()

退出当前登录用户。相当于Laravel的auth()->logout().

test('once a user logs out they cannot see the dashboard', () => {
  cy.login({ username: 'JohnDoe' });

  cy.logout();

  cy.visit('/dashboard').assertRedirect('/login');
});

cy.create()

使用Laravel工厂创建和持久化一个新的Eloquent记录。 

test('it shows blog posts', () => {
  cy.create('App\\Post', { title: 'My First Post' });

  cy.visit('/posts').contains('My First Post');
});

如上cy.create()相当于:

App\Post::factory()->create(['title' => 'My First Post']);

你也可以在第二个参数中指定记录数量。这样将会返回一个post集合。

test('it shows blog posts', () => {
  cy.create('App\\Post', 3, { title: 'My First Post' });
});

最后,你也可以在cy.create()中传入一个对象。如果你需要积极加载(eager load)关联模型或者在给定的模型工厂创建模型记录的话,这样做可能是更好的选择。

test('it shows blog posts', () => {
    cy.create({
        model: 'App\\Post',
        attributes: { title: 'My First Post' },
        state: ['archived'],
        load: ['author'],
        count: 10
    })
});

此举相当于PHP的代码:

$user = \App\Post::factory(10)->archived()->create([ 'title' => 'My First Post' ])->load('author');

auth()->login($user);

cy.refreshRoutes()

在Cypress测试套件运行之前,这个会自动获取Laravel应用的所有路由集合,将其存放在内存中。你无需手动调用此方法。虽然如此,如果你的路由会因为特别测试的副作用而产生改变,你也可以调用:

test('it refreshes the list of Laravel named routes in memory', () => {
    cy.refreshRoutes();
});

cy.refreshDatabase()

在测试数据库中触发migrate:refresh. 通常,在调用beforeEach时运行,确保每次新测试,你的数据库都是全新和干净的。

beforeEach(() => {
  cy.refreshDatabase();
});

test('it does something', () => {
  // php artisan migrate:fresh has been
  // called at this point.
});

cy.seed()

在当前Cypress环境中,运行所有的数据库数据填充,或者运行单个数据填充类。

test('it seeds the db', () => {
  cy.seed('PlansTableSeeder');
});

假设.env.cypress文件的APP_ENV设为”acceptance“, 如上调用相当于:

php artisan db:seed --class=PlansTableSeeder --env=acceptance

cy.artisan()

在当前Cypress测试环境中触发Artisan命令。记住在选项前使用双横线:

test('it can create posts through the command line', () => {
  cy.artisan('post:make', {
    '--title': 'My First Post',
  });

  cy.visit('/posts').contains('My First Post');
});

这个调用相当于:

php artisan post:make --title="My First Post"

cy.php()

此命令将允许你触发和随意评估PHP代码:

test('it can evaluate PHP', () => {
    cy.php(`
        App\\Plan::first();
    `).then(plan => {
        expect(plan.name).to.equal('Monthly'); 
    });
});

请慎重使用此命令。当验证应用或者数据库状态时,它或许挺有用的。同样可以用于设置测试的”世界“,尽管如此,使用cy.seed()对目标数据填充通常是更好的方式。

Routing路由

每次允许测试套件时,这个包会获取Laravel应用中的所有具名的路由,将其存放于内存中。你另外会发现一个./cypress/support/routes.json文件,包含一堆这样的JSON。此包重写了基础的cy.visit()方法,使之允许传路由名作为参数作为可选方式替代原来的URL。

test('it loads the about page using a named route', () => {
    cy.visit({
        route: 'about'
    });
});

如果路由名称中需要通配符,你可以使用parmeters属性包含:

test('it loads the team dashboard page using a named route', () => {
    cy.visit({
        route: 'team.dashboard',
        parameters: { team: 1 }
    });
});

如果你要获取应用的全部路由清单,使用Cypress.Laravel.routes属性。

// Get an array of all routes for your app. 
Cypress.Laravel.routes; // ['home' => []]

另外,如果你要将路由名称转换成它关联的URL,可以这样使用Cypress.Laravel.route()方法:

Cypress.Laravel.route('about'); // /about-page

Cypress.Laravel.route('team.dashboard', { team: 1 }); // /teams/1/dashboard

Security