DEV Community

Cover image for How to log in programmatically with Cypress
Walmyr
Walmyr

Posted on • Edited on

How to log in programmatically with Cypress

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 to proceed.

It seems counterintuitive, but that's precisely it.

From the graphical user interface, we should test it only once. More than that is a 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 may not be suitable for your use case.

To ease the explanation, I will use a project I have recently contributed to β€” the BR Modelo Web.

Let's imagine the following test case.

// cypress/e2e/programmaticLogin.cy.js

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

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

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

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 logged-in user's models. 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 cy.env() functionality). I do this not to expose sensitive data.

Then, I chain to the cy.request() command a .then(), which takes as an argument an arrow function, which takes as an argument the response's body of the cy.request(), using JavaScript's object destructuring.

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 login functionality, it is recommended that testing take place via the graphical user interface. However, for all other features that require an authenticated user, use programmatic login and save a few seconds on each test!

Bonus - Custom Command

Since multiple test suites will need to log in programmatically, we can move that logic into a custom command that can be reused as many times as needed.

Here's what the test code would look like.

// cypress/e2e/programmaticLogin.cy.js

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

And the custom command.

// cypress/support/commands.js

Cypress.Commands.add('loginViaAPI', () => {
  cy.env(['userEmail', 'userPassword']).then(({ userEmail, userPassword }) => {
    cy.request('POST', `${Cypress.expose('apiUrl')}/users/login`, {
      username,
      password,
    }).then(({ body }) => {
      cy.setCookie('sessionId', body.sessionId)
      cy.setCookie('userId', body.userId)
      cy.setCookie('userName', body.userName)
      cy.visit('/#!/main')
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

In the test, now all the logic of cy.request and cy.setCookie is abstracted. I just call the cy.loginViaAPI() command, and it handles the authentication.

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! πŸͺ„

That's it!

I hope you enjoyed it.


Did you like the content? Leave a comment.


This post was originally published in Portuguese at the Talking About Testing blog.


Want to go deeper?

I built a hands-on learning platform to take you from your first test to a complete end-to-end testing workflow with confidence. You'll find interactive lessons, coding challenges, and quizzes that cover real user interactions, accessibility checks, network mocking, API testing, and the automatic running of tests in CI/CD.

New courses are added over time, so there's always more to explore.

Explore the courses β†’

Top comments (4)

Collapse
 
gllisboa profile image
gllisboa

How i can know what cookie the system expect ? Its possible catch this on response or request ?

Collapse
 
walmyrlimaesilv profile image
Walmyr • Edited

Ideally, you would have to understand how the frontend works.

Collapse
 
gllisboa profile image
gllisboa

Thx

Thread Thread
 
walmyrlimaesilv profile image
Walmyr

You're welcome!