运行Cypress

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-07-03

命令行运行 Cypress

cypress open

cypress open 方式运行 Cypress,是指以交互模式打开 Cypress 测试运行器(Test Runner)运行测试用例,测试用例中的每一条命令,都会显式的显示在Test Runner中,用户可以通过 Test Runner 随时暂停,恢复测试用例执行。

npx cypress open

# 如果 package.json 配置了scripts  "cypress:open": "cypress open"
npm run cypress:open

Cypress open 支持的参数有:

  • –browser, -b: 用来指定待运行的浏览器。 npm run cypress:open --browser chrome

  • –config, -c:用来指定运行时的配置项。npm run cypress:open --config pageLoadTimeout=100000

  • –config-file, -C:用来指定运行时的配置文件。npm run cypress:open --config-file tests/cypress-config.json

  • env, -e:用来指定环境变量。 npm run cypress:open --env host=api.dev.local,version=1,mode=dev,代码中可以这么访问 Cypress.env('mode')

  • –global:用来以 global 模式打开 Cypress,global 模式允许在多个嵌套项目中共享同一个安装好的 Cypress 版本。

  • –port, -p:用来指定运行时的端口。npm run cypress:open --port 8080

  • –project,-P:如果你的项目包含多个子项目,可以用此参数来运行指定的子项目。npm run cypress:open --project ./some/nested/folder

  • –help, -h:用来输出 help 信息。

cypress run

cypress run 命令直接运行测试脚本直至测试结束,Cypress 默认启动无头(Headless)Electron 浏览器来运行测试。

运行的方式与 cypress open 基本一致。支持一下参数,

  • –ci-build-id:用于分组运行或并行运行,它通过指定一个唯一的标识符来实现,必须配合 –group 或 –parallel 才能使用。npm run cypress:run --ci-build-id BUILD_NUMBER

  • –group:用来将以此运行中将符合条件的测试用例分组展示。 npm run cypress:run --group smoke --spec 'cypress/integration/somke/**/*',通常跟 –ci-build-id 一起使用。

  • –headed:用来指定有头运行,npm run cypress:run --headed

  • –no-exit:用来指定 Test Runner 在运行后不退出,可以使用参数 –headed 和 –no-exit 来指定测试运行时显示及在运行后查看命令日志。npm run cypress:run --headed --no-exit

  • –key, -k:通常跟 Dashboard 一起使用,用来指定那些需要在运行时录制的项目秘钥,将在后续的章节介绍。

  • –parallel:用来在多台机器上并行运行测试用例集,将在后续的章节介绍。

  • –record:用来指定在测试运行时录制视频。npm run cypress:run --record --key,如果你在 cypress.json 配置了环境变量 CYPRESS_RECORD_KEY则可以忽略 –key 标志。

  • –reporter, -r:用来指定 Reporter,前文已经介绍过,一起的还有 –reporter-options, -o。具体请查看

  • –spec, -s:参数用来指定运行哪些测试文件夹/文件。如果你不指定测试文件夹,Cypress将会自动运行所有存在 Integration 文件夹下的测试用例。

–browser 、 –config 、–config-file 、–env 等参数与 cypress open 一致。

测试运行器

测试运行器能够方便用户调试,监控测试运行状态,从而使测试更加便捷。

在你以交互式命令 cypress open 运行测试用例时,你可以在 Test Runner 里指定待测试的浏览器,Cypress 默认使用 Electron 浏览器。

Test Runner

时间穿梭功能

如果测试过程中发生错误,大部分的测试框架都无法得知测试执行时被测应用程序所处的状态,只能在测试运行结束后通过日志、截图来猜测测试失败的原因。Cypress Test Runner 则完全相反,它忠实地记录了每一条测试命令被执行时应用程序所处的状态,并且保存起来以便随时回溯,这种能力被称为时间穿梭。

需要注意的是,Cypress 保存的是应用程序状态,不是截图,故 Cypress 支持查看执行命令时发生的一切操作,用户可直接定位到错误的根本原因,无需猜测。

在测试结束后,可以通过鼠标悬停,或者使用鼠标单击某个命令的方式来进行实践穿梭。

使用鼠标悬停。可以在应用程序预览中查看命令作用到被测应用的具体情况。

Test Runner

使用鼠标单击,将在浏览器的 Console 中看到当命令执行时,应用到了被测应用程序的哪个元素上,以及当时的上下文详细信息。

Test Runner

重塑你的测试习惯

以下介绍,使用 Cypress 需要注意的地方,与其他测试框架『不太一致』的地方。

慎用箭头函数

describe('测试箭头函数', () => {
  beforeEach(function () {
    // wrap 'hello' 到 text 中
    cy.wrap('hello').as('text');
  })

  it('访问不到', () => {
    // this.text 打印为空
    cy.log('hhhhhhhh', this.text)
  })

  it('访问得到', function () {
    // this.text 打印为 hello
    cy.log('hhhhhhhh', this.text)
  })
})

async/await 不工作

Cypress 不支持 async 和 await 代码,虽然 Cypress 类似于 Promise 但不同于 Promise,Promise 本身没有重试的概念,但 Cypress 却支持命令自动重试。

躲不过的同源策略

同源策略是浏览器安全的基石,这也意味着当两个 iframe 直接有访问时,必须同时满足协议相同、域名相同、端口相同三个条件。由于 Cypress 是运行在浏览器之中的,要测试应用程序,Cypress 必须始终能够和应用程序直接通信

但显然浏览器的同源策略不允许,Cypress 通过以下方式绕过了浏览器的限制:

  • 将 document.domain 注入 text/html 页面。
  • 代理所有的 HTTP/HTTPS 通信。
  • 更改托管的 URL 以匹配被测应用程序的 URL。
  • 使用浏览器内部的 API 进行网络间的通信。

首次加载 Cypress 时,内部 Cypress Web 应用程序托管在一个随机端口上,类似 http://localhost:65874/__/

在一次测试中,当第一个 cy.visit() 命令被发出后,Cypress 将更改其 URL 以匹配远程应用程序的来源,从而解决同源策略的主要障碍。但这样带来的坏处是,在一次测试中,访问的域名必须处于同一个超域下,否则 Cypress 测试将会报错。

describe('测试同源', () => {
  it('立即报错', () => {
    // 会立即报错
    cy.visit('https://helloqa.com')
    cy.visit('https://www.baidu.com')
  })
})

闭包

在Cypress中,保存一个值或者引用的最好方式是使用闭包,.then() 是 Cypress 对闭包的一个典型应用,.then() 返回的是上一个命令的结果,并将其注入下一个命令中。

// 获取 btn 的值前后进行比较
cy.get('button').then(($btn) => {
  const txt = $btn.text();

  cy.get('form').submit();

  cy.get('button').should(($btn2) => {
    expect($btn2.text()).not.to.eq(txt);
  })
})

变量和别名

在 Cypress 中,可使用如下方式进行元素赋值操作或者实现比变量共享。

1、.wrap():返回传递给它的对象

const getName = () => {
  return 'iTesting';
}

// wrap 返回的对象做了操作使其能够调用 cy 的命令,有点类似于 $()
// const eDiv = document.getElementById('main');
// eDiv.find('input')  // Error
// $(eDiv).find('input')  // Success
cy.wrap({ name: getName }).invoke('name').should('eq', 'iTesting');

2、.as():用于分配别名以供以后使用,可以使用 cy.get()cy.wait() 加有 @ 前缀在别处再次访问,一般与 cy.wrap()/cy.fixture() 配对使用。

decribe('a suite', () => {
  beforeEach(() => {
    cy.visit('https://www.baidu.com');
    cy.contains('更多').then(($el) => {
      // 将 '更多' 这个元素的文本存作别名 text
      cy.wrap($el.text()).as('more');
    })
  })

  it('测试 alias', () => {
    // 在别处访问别名
    cy.get('@more').then((val) => {
      cy.log(val)
    })
  })
})

3、fixture()

如果在测试中需要的变量直接来自外部文件,可以通过 fixture() 来实现上下文共享。

.fixture() 用来加载位于文件中的一组固定数据。语法如下:

cy.fixtrue(filePath);
cy.fixtrue(filePath, encoding);
cy.fixtrue(filePath, options);
cy.fixtrue(filePath, encoding, options);

常与 .as() 一起使用:

// user.json 位于 cypress/fixture/ 目录下
cy.fixture('user.json').as('usersData')

cy.get('@usersData').then((data) => {
  cy.log(data);
})

4、Cypress.env()

(1)配置 cypress.json,定义自定义变量用来之后保存运行中生成的变量

{
  "env": {
    "myCustomVari": "",
    "myCustomObj": {}
  }
}

(2)在测试中使用 Cypress.env() 来设置和获取变量

describe('', () => {
  beforeEach(function () {
    cy.visit('/login')
  })

  it('在前一个测试用例中设置', () => {
    cy.contains('In this recipe we').then(($el) => {
      Cypress.env('myCustomVari', $el.text())
      Cypress.env('myCustomObj').moreTxt = $el.text()
    })
  })

  it('在下一个测试用例中获取', () => {
    cy.log('获取设置的变量', Cypress.env('myCustomVari'))
    cy.log('获取设置的对象', Cypress.env('myCustomObj'))
  })
})