Search code examples
javascripttestingautomated-testscypressintegration-testing

In Cypress, should assertions be kept within test blocks themselves, or should they be in helper functions/automation steps


I recently started working on Cypress and I see a lot of assertions inside of helper functions/automation steps, this seems to me like it would be a bit of an anti-pattern since it would repeat these assertions in every and any other test that is also using that automation step, e.g. logging in,

Having this should in the helper function/step will cause every other test which is logged in to also have unnecessary assertions I would think, and longer functional tests would begin to have a lot of assertions which aren't needed for a specific test. Am I overthinking this issue, or is it valid? I also was interested if there were any performance impacts of this, I assume it's pretty fine for a simple one like this, but for much longer multi-step tests, would this begin to be a concern? I couldn't find any other resources on this specifically, so I would love any insight or direction if I'm overthinking it, or it is something to refactor and consider. (For reference as well, app actions and component testing are not in use here, and not currently an option)

cy.get('[data-cy="login-email"]').type('username')
cy.get('[data-cy="login-password"]').type('password')
cy.get('[data-cy="login-btn"]').click()
cy.get('[data-cy="profile-btn"]').should('exist').and('contain', 'username')

My fix would be to put assertions into the respective test case's IT/Specify block, instead of the helper functions.


Solution

  • Your question is valid. You can create a custom action for login, and indeed when working on bigger applications the impact of performing the login as an assertion gets more noticeable as we might cause some performance issues and make our test suites more time-consuming, plus this would mean that we have a dependency between our tests which is generally a bad practice:

    Tests should always be able to be run independently from one another and still pass.

    So that's why the documentation suggested this talk: https://youtu.be/5XQOK0v_YRE?t=512

    Which talked about these recommendations:

    cy best practices

    Don't use the UI to build up the state, use it directly

    Don't share page objects to share UI knowledge, write specs in isolation

    Don't limit yourself to trying to act like a user, you have native access to everything

    So basically you might make your login command use your authentication API endpoints and save the JWT tokens in your state and then just pull it from there instead of re-running your login assertions and actions.

    Cypress.Commands.add('login', () => { 
      cy.request({
        method: 'POST',
        url: 'BASE_URL/login',
        body: {
          user: {
            email: '[email protected]',
            password: 'a_pwd',
          }
        }
      })
      .then((resp) => {
        // if your application loads the jwt from localstorage for example
        window.localStorage.setItem('jwt', resp.body.user.token)
      })
    
    })
    

    Then in your tests:

    beforeEach(() => {
      cy.login()
    })
    

    This might be challenging if you want to test social logins, for that please have a closer look at: