Cypress Hooks
Hooks run code at specific points during a test file's execution. They help set up conditions before tests run and clean up afterward. This topic explains the four main hooks Cypress offers.
The Four Hook Types
before(() => { /* runs once before all tests */ })
beforeEach(() => { /* runs before every test */ })
afterEach(() => { /* runs after every test */ })
after(() => { /* runs once after all tests */ })A Simple Way to Picture It
Think of hooks as the opening and closing routines at a store. The store manager unlocks the door once at the start of the day, similar to a before hook. A cashier resets the counter before serving each customer, similar to a beforeEach hook. The store locks up once at closing time, similar to an after hook.
Using beforeEach for Setup
The beforeEach hook runs before every single test inside a describe block. Teams commonly use it to visit a page or reset application state.
describe('Login Page', () => {
beforeEach(() => {
cy.visit('/login')
})
it('shows the login form', () => {
cy.get('form').should('be.visible')
})
it('shows a forgot password link', () => {
cy.contains('Forgot Password').should('exist')
})
})Both tests automatically visit the login page first, without repeating the cy.visit line in each test.
Hook Execution Order
before (runs once) | v beforeEach --> Test 1 --> afterEach | v beforeEach --> Test 2 --> afterEach | v after (runs once)
Using after for Cleanup
The after hook runs once when every test inside a describe block finishes. Teams use it to clean up data created during testing, such as deleting a test account.
after(() => {
cy.request('DELETE', '/api/test-user')
})Using afterEach for Logging
The afterEach hook runs after every individual test finishes, regardless of pass or fail status. Some teams use this hook to capture extra logs or take a screenshot for review.
afterEach(() => {
cy.log('Test finished')
})Nesting Hooks Inside Multiple Describes
Hooks apply only within their own describe block unless explicitly shared. Nested describe blocks can have their own hooks in addition to the parent's hooks.
describe('Account Settings', () => {
beforeEach(() => {
cy.visit('/settings')
})
describe('Password Section', () => {
beforeEach(() => {
cy.get('#password-tab').click()
})
it('shows password fields', () => {
cy.get('#new-password').should('exist')
})
})
})This structure runs both beforeEach hooks in order before the password section test executes.
Avoiding Overused Hooks
Placing too much logic inside a single hook makes tests harder to understand at a glance. A hook should perform simple, predictable setup rather than complex conditional logic. Keeping hooks short helps other testers understand the test flow quickly.
When Not to Use a Hook
A one-time action needed by only a single test does not belong inside a shared hook. Placing such actions directly inside the relevant it block keeps unrelated tests unaffected.
Key Points
- Hooks run code at specific points during test execution.
- beforeEach and afterEach run around every individual test.
- before and after run only once for an entire describe block.
- Nested describe blocks can define their own additional hooks.
- Hooks should stay short and predictable for easy understanding.
