Jeff
Jeff

Reputation: 1879

Cypress: Why finicky in handling spec's after hook?

Blocked by a Cypress custom command not executing properly within a spec's after hook when a test failed. Experimented with the following minimal spec examples.

Context

Testing two Cypress custom commands:

Spec #1, cy.cmdLogout() in after hook does work

Very simple Cypress tests spec, test cy.cmdLogin() with after hook with cy.cmdLogout(), works perfectly:

 context('SPEC Login', () => {
    after('AFTER Logout', () => {
      if (this.successLogin) {
        cy.cmdLogout().then(() => {
          cy.log('Logout Success');
        });
      }
    });

    it('TEST Login', () => {
      cy.cmdLogin().then(() => {
        cy.log('Login Success');
        this.successLogin = true;
      });
    });
  });

Spec #2, cy.cmdLogout() in after hook does not work

Modified Cypress tests spec, test cy.cmdLogin() with after hook with cy.cmdLogout(), now added a test that forced failure expect(false).to.be.a('boolean').to.be.true;. The after hook cy.cmdLogout() is called and fails:

Verified that Cypress could handle cy.get('logout_button') without failure.

Yet, extending with .click(), cy.get('logout_button').click(), Cypress would throw an error: Can not perform click() on undefined.

 context('SPEC Login', () => {
    after('AFTER Logout', () => {
      if (this.successLogin) {
        cy.cmdLogout().then(() => {
          cy.log('Logout Success');
        });
      }
    });

    it('TEST Login', () => {
      cy.cmdLogin().then(() => {
        cy.log('Login Success');
        this.successLogin = true;
      });
    });

    it('TEST Fail', () => {
      expect(false).to.be.a('boolean').to.be.true;
    });
  });

Spec #3, removed after hook, cy.cmdLogout() moved to another test does work

Modified Cypress tests spec again and removed after hook, test cy.cmdLogin(), test forced failure, and now test cy.cmdLogout(). This works perfectly:

  context('SPEC Login', () => {
    it('TEST Login', () => {
      cy.cmdLogin().then(() => {
        cy.log('Login Success');
        this.successLogin = true;
      });
    });

    it('TEST Fail', () => {
      expect(false).to.be.a('boolean').to.be.true;
    });

    it('TEST Logout', () => {
      if (this.successLogin) {
        cy.cmdLogout().then(()=> {
          cy.task('log', 'Logout Success');
        });
      }
    });
  });

Insight as to why Cypress is finicky with spec's after hook would be very much appreciated

Upvotes: 2

Views: 620

Answers (1)

Fody
Fody

Reputation: 31944

The short answer (for the general situation) is change after() hook to before() hook, but in your case that wouldn't work because this.successLogin will always be undefined inside a before.

You would need to create a cy.isLoggedIn() command or call cy.cmdLogout() unconditionally.

To get after() the logic working on a fail, use

after('AFTER Logout', () => {
  cy.cmdLogout()
});

Cypress.on('fail', (error, runnable) => {
  cy.cmdLogout()
  throw error 
})

which is equivalent to this in plain javascript

try {
  some-code-that-might-error()
  cy.cmdLogout()                // does not run if error occurs
} catch (error) {
  cy.cmdLogout()                // do the logout in error situation
} 


  

Upvotes: 1

Related Questions