1

I am trying to write a custom Cypress command that sends a POST request to an endpoint, & I then want to store the response body in my test.

Here is what the response body looks like in Postman:

enter image description here

Here is my custom command in cypress/support/commands.js, for simplicity, I've removed the request body values:

Cypress.Commands.add('createStudent', (email) => {
    cy.request({
        method: `POST`,
        url: `myUrl`,
        body: {}
      }).then((resp) => {
        return resp
      });
});

Here is the code in my spec file:

let response = cy.createStudent(email);
cy.log(response)

However, when I run the code I get back the below object rather than the response body:

enter image description here

Can someone please tell me where I am going wrong, & what changes are required to return the actual HTTP response body?

Fody
  • 23,754
  • 3
  • 20
  • 37
user9847788
  • 2,135
  • 5
  • 31
  • 79
  • `return cy.request(...)`? But that still won't return the value, because it's asynchronous. You should read https://docs.cypress.io/guides/core-concepts/variables-and-aliases. And note `.then((resp) => { return resp })` is completely pointless. – jonrsharpe May 20 '22 at 15:50
  • I haven't written `return cy.request`, but thank you for your constructive feedback – user9847788 May 20 '22 at 15:52

2 Answers2

1

If you'll only ever be using the value in a Cypress chain, you could simply alias the command.

Cypress.Commands.add('createStudent', (email) => {
    cy.request({
        method: `POST`,
        url: `myUrl`,
        body: {}
      }).as('student');
});
...
cy.createStudent();
cy.get('@student').then((response) => {
  cy.log(response.body) // assuming you'd want to log the response body.
});
// OR
cy.get('@student').its('body').should('eq', { foo: 'bar' });
// the above example doesn't work with logging, but I'm guessing you don't _just_ want to log the response

If you may need the variable at other times outside of a Cypress chain, you could always stash the variable in Cypress.env().

Cypress.Commands.add('createStudent', (email) => {
    cy.request({
        method: `POST`,
        url: `myUrl`,
        body: {}
      }).then((res) => {
          Cypress.env('student', res);
      });
});
...
cy.createStudent().then(() => {
  cy.get('foo').should('have.text', Cypress.env('student').body.foo);
});
// key point is referencing the entire response by `Cypress.env('student')`
agoff
  • 5,818
  • 1
  • 7
  • 20
  • In your last example, `Cypress.env('student').body.foo` value will be captured *before* `cy.request` gets actually performed – Mikhail Bolotov May 20 '22 at 18:34
  • No, it will be populated by the `createStudent()` which will run to completion _before_ `cy.get()` executes. This is the same concept as how `cy.visit('foo'); cy.get('foo')` won't fail -- Cypress queues the commands to run after one another. – agoff May 20 '22 at 19:41
  • ` it.only('test', () => { cy.request('http://www.google.com').then((res) => { Cypress.env('google', res); }); cy.get('a').should('have.text', Cypress.env('google')); });` -> Try that example (sorry for terrible formatting, we are in the comments after all) – agoff May 20 '22 at 19:42
  • this is how your example run under debug: https://imgur.com/a/zE3NcDg – Mikhail Bolotov May 20 '22 at 20:04
  • That's not my example. You've added in an additional step where you assign the value to a variable. There is a good chance that variable is not set correctly because Cypress async code runs _after_ that variable is set. Please try my example again, without the additional step. Or consider reading on why variable assignment can get wonky. https://filiphric.com/cypress-basics-variables – agoff May 23 '22 at 13:28
  • I agree that I added this variable to make it clear that the `Cypress.env('google')` *expression is resolved to undefined at this moment*. This variable may be inlined and *it will be the same undefined value* passed into the should command. This is because **the command's parameters are captured at the moment of command queueing** and not the command execution. And at the time of queueing, your environment variable has not been set yet. – Mikhail Bolotov May 23 '22 at 16:37
  • That's just not true. Please run the code example I provided exactly. I use this paradigm many places in my project and do not experience the behavior you're describing. If what you were describing was true, then the following would not work (which it does) => `it.only('test', () => { cy.request('https://www.google.com').then((res) => { Cypress.env('google', res); }); cy.log(Cypress.env('google')) .get('a') .should('have.text', Cypress.env('google')); });` – agoff May 23 '22 at 17:05
  • here is the screenshot of how the exact example is run in Cypress test runner: https://imgur.com/a/1kvaJPS It's expecting the text to be **undefined**. Do you have other result when running? Are you sure you do not have this environment variable defined somewhere else? – Mikhail Bolotov May 23 '22 at 17:36
  • I feel like an ass - I had an extra `.then()` in the one I was running, but somehow I kept missing that. Apologies -- I've updated my answer. – agoff May 23 '22 at 18:51
  • Apology accepted. Now the answer looks good for me. – Mikhail Bolotov May 23 '22 at 20:38
1

If you look at the console message, there's a type $Chainer shown which is a wrapper object around the result you actually want (response).

The Chainer is fundamental to Cypress being able to retry queries that fail initially but may succeed within a timeout period (usually 4 seconds).

But it means you can't use the return value. Instead you need to "unwrap" the value using .then().

Cypress.Commands.add('createStudent', (email) => {
  cy.request({
    method: 'POST',
    url: 'myUrl',
    body: {...}
  })
  // The response is put on the "chain" upon exit of the custom command
  // You need nothing else here to get the raw response
})

cy.createStudent().then(response => {
  cy.log(response)
});

You can add a step to extract details from the response, like

Cypress.Commands.add('createStudent', (email) => {
  cy.request({
    method: 'POST',
    url: 'myUrl',
    body: {...}
  })
  .then(response => {
    expect(response.success).to.eq(true)   // check expected response is good
    return response.body.id                // pass on just the id
  })
})

cy.createStudent().then(id => {
  cy.log(id)
});
Fody
  • 23,754
  • 3
  • 20
  • 37