Sign Up

Sign In

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

You must login to ask question.

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

API Automation in Cypress

How to login programmatically with Cypress

#cypress #testing #authentication  #javascript

Learn how to make your automated tests faster by authenticating via API

Automated graphical user interface tests must be independent of each other. In addition, such tests must rely as little as possible on the graphical user interface to reach the desired state for the test itself to take place.

It seems counter-intuitive, but that’s precisely it.

From the graphical user interface, we should test it only once. More than that is waste.

However, in most web applications, the user must be authenticated to access certain functionality. So, how to authenticate such a user without going through the login page?

That’s precisely what I will show you in this pinch of Cypress.

Note: It is worth remembering that this is just an alternative and that it may not be suitable for your use case. For more examples, I recommend reading the Testing Strategies section of the Cypress official documentation.

To facilitate the explanation, I will use a real project to which I have contributed recently. The BR Modelo Web.

Let’s imagine the following test case.

it('successfully logs in programmatically', () => {
  cy.intercept('GET', "${Cypress.env('apiUrl')}/models?userId=*")
    .as('getUserModels')

  cy.request('POST',"${Cypress.env('apiUrl')}/users/login", {
    username: Cypress.env('userEmail'),
    password: Cypress.env('userPassword'),
  }).then((response) => {
    cy.setCookie('sessionId', response.body.sessionId)
    cy.setCookie('userId', response.body.userId)
    cy.setCookie('userName', response.body.userName)
  })

  cy.visit('/#!/main')
  cy.wait('@getUserModels')
  cy.contains('h2', 'Models').should('be.visible')
}

Now, let’s understand what this code does.

First, inside the test body, that is, inside the it block, I use the cy.intercept command. With such a command, I can “listen” 👂 to network calls, such as a GET request to the application’s API URL that fetches the models of the logged-in user. Then I give an alias to that intercept. The alias is getUserModels.

Then comes the part where the programmatic authentication happens.

In this part, I use the cy.request functionality to make a POST request to the login URL, passing the username and password properties in the request body, both coming from variables (using the Cypress.env() functionality). I do this not to expose sensitive data.

Then, I chain to the cy.request() command a .then(), which takes as argument an arrow function, which takes as an argument the response of the cy.request().

In the body of this arrow function, I use the cy.setCookie() functionality, as the name suggests, to set some cookies based on the body of the request-response. These are precisely the cookies set when the user logs in via the graphical user interface.

With cookies set, I visit the application’s homepage.

Finally, I do some checks.

First, I wait for the intercept request created earlier to occur, with cy.wait(), passing it the alias created earlier ('@getUserModels').

And then, I check that a particular element is visible (an h2 with the text Models), which is only visible to authenticated users, proving that the login was successful.

Done! 🎉

Attention: When testing a login functionality, it is recommended that such testing takes place via the graphical user interface. However, for all other features that require the authenticated user, use login programmatically and save a few seconds on each test!

Bonus – Custom Command

Since more than one test suite will need to login programmatically, we can move that logic to a custom command, which can be reused as many times as needed.

Here’s what the test code would look like.

// cypress/integration/programmaticLogin.spec.js

it('successfully logs in via GUI', () => {
  cy.intercept('GET',"${Cypress.env('apiUrl')}/models?userId=*")
    .as('getUserModels')
  cy.loginViaAPI()
  cy.wait('@getUserModels')
  cy.contains('h2', 'Models').should('be.visible')
})

And the custom command.

// cypress/support/commands.js

Cypress.Commands.add('loginViaAPI', (
  email = Cypress.env('userEmail'),
  password = Cypress.env('userPassword')
) => {
  cy.request('POST',"${Cypress.env('apiUrl')}/users/login", {
    username: email,
    password,
  }).then((response) => {
    cy.setCookie('sessionId', response.body.sessionId)
    cy.setCookie('userId', response.body.userId)
    cy.setCookie('userName', response.body.userName)
    cy.visit('/#!/main')
  })
})

In the test, now all the logic of cy.request and cy.setCookie is abstracted. I just call the cy.loginViaAPI() command, and it manages to do what needs to be done to authenticate the user.

In addition to having the previous logic of programmatic login, the custom command can now also receive an email and password as arguments. However, if no arguments are passed, such values already have defaults coming from variables.

Also, I decided to move the visit to the home page to the custom command.

See the test running and authenticating without going through the login page. It looks like magic! 🪄

Image description

That’s it!

I hope you enjoyed it.

Related Posts

Cannot find name 'Cypress'

Draw a line using Cypress

Parallelizing Cypress with Jenkins

Leave a comment

You must login to add a new comment.