How to reuse yielded value later in the test

2020-02-02 03:31发布

问题:

Context: I have an element with innerText in Page1 and another element with innerText on Page 2. And I need to compare these two values in Page 3. So I need to find a way to save these values in a variable globally so that they can be used later.

Things I tried:

Trial 1: Didn't work as page1value scope is limited to the cy.get()

  cy.get('#selector').invoke('text').then(text => {
      const page1value = text
  })

Trial 2: Didn't work as whenever I try to print the value outside, it comes as undefined or the value with which it was initialized it with.

it('TC Name', () => {

    let page1value,
        cy.get('#selector').invoke('text').then(text => {
            page1value = text
        })

    cy.log(page1value) //comes as undefined

})

Trial 3: Using .as() as well its coming as undefined.

let page1value;
cy.get('#selector').invoke('text').as('page1value');
cy.log(page1value) //comes as undefined

It would be great if someone could tell me where I am doing wrong.

回答1:

Cypress commands are pushed (enqueued) into a queue (called the Command queue --- which is basically an array), and then executed serially (one after another), and asynchronously.

While your cy.log() will be executed asynchronously, too, after the previous command, the value you pass to it (page1value) is passed/evaluated synchronously, at the time you push the command to the queue (which is evaluated at the time the callback passed to it() is called --- at the beginning of the test).

This is just regular JavaScript behavior, and has nothing to do with Cypress. All the commands cy.* are just methods (functions) on the cy object, and they're called immediately. What is not called (executed) immediately, is the logic that each command does (e.g. query the DOM for the selector you supply to cy.get(), log to Command log when you call cy.log('string'), etc.).

Thus, in your 2nd example:

  1. You declare page1value.
  2. You then immediately enqueue commands cy.get(), cy.invoke, cy.then().
  3. And you also immediately enqueue cy.log, to which you pass page1value (which at this time is still undefined).
  4. After all commands are enqueued, they start to execute, from top to bottom. When the cy.then command takes turn to execute, the page1value variable is assigned, but it's no longer used (read) anywhere for the rest of the test (recall that you've already read it when you passed it to the cy.log command in the previous step).

Thus, what you want to do instead, is:

cy.get('#selector').invoke('text').then(text => {
  cy.log(text);
});

In your 3rd example, if you alias something, you need to access that value using another command (remember, everything is asynchronous, so you can't access values that are set asynchronously, in a synchronous manner as you're doing), in this case cy.get('@aliasName'):

cy.get('#selector').invoke('text').as('page1value');
cy.get('@page1value').then( value => {
  cy.log(value);
});

Note that the above explanations are slightly inaccurate and inexhaustive (there are more things going on behind the scenes), for the sake of simplicity. But as an intro to how things work, they should do.

Anyway, you should definitely read Introduction to Cypress.

You may also take a look at my older answers that touch on related concepts:

  • How to use a variable as a parameter in an API call in Cypress
  • In Cypress when to use Custom Command vs Task?