使用 "this" 访问 Cypress 中的别名

Accessing aliases in Cypress with "this"

我正在尝试使用别名在我的 beforebeforeEach 挂钩之间共享值。如果我的值是一个字符串,它目前可以工作,但是当该值是一个对象时,别名只在第一个测试中定义,之后的每个测试 this.user 在我的 beforeEach 钩子中都是未定义的。如何在测试之间共享作为对象的值?

这是我的代码:

before(function() {
  const email = `test+${uuidv4()}@example.com`;
  cy
    .register(email)
    .its("body.data.user")
    .as("user");
});

beforeEach(function() {
  console.log("this.user", this.user); // This is undefined in every test except the first
});

别名在每个测试中都是未定义,除了第一个,因为别名在之后被清除] 每次测试。

通过 cy.get('@user') 语法访问别名变量。一些命令本质上是异步的,因此使用包装器访问变量可确保在使用之前解析它。

参见文档 Variables and Aliases and get


似乎没有办法明确保留别名,就像 cookies

Cypress.Cookies.preserveOnce(names...)

但是这个 recipe for preserving fixtures 展示了一种通过在 beforeEach()

中恢复它们来保留全局变量的方法
let city
let country

before(() => {
  // load fixtures just once, need to store in
  // closure variables because Mocha context is cleared
  // before each test
  cy.fixture('city').then((c) => {
    city = c
  })

  cy.fixture('country').then((c) => {
    country = c
  })
})

beforeEach(() => {
  // we can put data back into the empty Mocha context before each test
  // by the time this callback executes, "before" hook has finished
  cy.wrap(city).as('city')
  cy.wrap(country).as('country')
})

如果您想访问全局 user 值,您可以尝试类似

let user;

before(function() {
  const email = `test+${uuidv4()}@example.com`;
  cy
    .register(email)
    .its("body.data.user")
    .then(result => user = result);
});

beforeEach(function() {
  console.log("global user", user); 
  cy.wrap(user).as('user');              // set as alias
});

it('first', () => {
  cy.get('@user').then(val => {
    console.log('first', val)            // user alias is valid
  })
})

it('second', () => {
  cy.get('@user').then(val => {
    console.log('second', val)           // user alias is valid
  })
})

TL;DR:如果你想在每个测试中使用一个别名 user 对象,你必须在 beforeEach 挂钩而不是 before 挂钩中定义它。

赛普拉斯在测试之间执行大量清理,这包括清除所有别名。根据 Variables and Aliases 的共享上下文部分:“每次测试后都会自动清除别名和属性。”因此,您看到的结果(您的别名在第一次测试后被清除,随后未定义)是预期的行为。

我无法确定 register 在原始 post 中做了什么,但您的意图似乎是节省在 beforeEach 中重复执行 API 调用的开销钩。将您想要的所有内容都放在 beforeEach 挂钩中并忽略开销绝对是最简单的(另外,没有 UI 交互的纯 API 调用不会产生太多损失)。

如果您真的需要避免重复,这不应该通过常规变量来完成,因为赛普拉斯的自定义链可能存在时序问题。这是一个anti-pattern they publish。最好的方法是:

  • 创建一个 fixture file 静态用户数据,您将使用它来进行测试。 (删除 uuidv4。)
  • 对于需要您的用户数据的测试集,使用夹具数据在 before 挂钩中调用 register。这将在被测系统中创建数据。
  • 使用 beforeEach 挂钩加载夹具数据并为每个测试设置别名。现在,无需 API 调用即可访问您需要的静态数据,并且由于 before 挂钩,它可以保证正确地存在于系统中。
  • 运行 您使用别名进行的测试。
  • 清理after钩子中的数据(因为您的用户不再有随机电子邮件,您需要添加此步骤)。

如果您需要对整个测试套件执行上述操作,请将 beforeafter 挂钩放在 support file 中,使它们成为全局的。

替换

console.log("global user", this.user); 

cy.log(this.user);

它应该按预期工作。

这是因为 cypress 命令的异步特性。将其视为一个两步过程:所有的 cypress 命令在 运行 时都没有按照您的想法进行。他们只是建立了一个命令链。该链稍后作为测试执行。

对于 console.log() 等其他命令显然不是这种情况。此命令在准备测试时执行。

这是explained in great detail in the cypress documentation:

但我觉得很难理解这个问题。你必须习惯它。 一个经验法则:测试中的几乎每个命令都应该是 cypress 命令。

所以只需使用 cy.log 而不是 console.log

如果你必须使用console.log你可以这样做:

cy.visit("/).then(() => console.log(this.user))

这样 console.log 就链接起来了。或者,如果您没有要链接的主题,请像这样构建您自己的自定义命令:

Cypress.Commands.add("console", (message) => console.log(message))
cy.console(this.user)

在 cypress 中使用 this 的另一个错误是使用箭头函数。如果这样做,您将无法访问您期望的 this。请参阅赛普拉斯文档中的 Avoiding the use of this