0
0
Cypresstesting~15 mins

before and after hooks in Cypress - Deep Dive

Choose your learning style9 modes available
Overview - before and after hooks
What is it?
Before and after hooks are special functions in Cypress that run code automatically before or after your tests. They help set up things you need before tests start and clean up after tests finish. This makes tests easier to write and keeps them organized. Hooks run once or before/after each test depending on how you use them.
Why it matters
Without before and after hooks, you would have to repeat setup and cleanup code inside every test. This wastes time and can cause mistakes if you forget something. Hooks save effort, reduce errors, and make tests faster and more reliable. They help keep tests clean and focused on what they check, not on preparing or cleaning.
Where it fits
You should know basic Cypress test structure and commands before learning hooks. After hooks, you can learn about advanced test organization like nested hooks, custom commands, and test retries. Hooks are a foundation for writing maintainable end-to-end tests.
Mental Model
Core Idea
Before and after hooks automatically run setup and cleanup code around tests to keep tests simple and reliable.
Think of it like...
It's like preparing ingredients before cooking and cleaning the kitchen after. You do these tasks once so the cooking (testing) goes smoothly without repeating the same chores every time.
┌───────────────┐
│ Before Hook   │
├───────────────┤
│ Test 1        │
├───────────────┤
│ After Hook    │
├───────────────┤
│ Before Hook   │
├───────────────┤
│ Test 2        │
├───────────────┤
│ After Hook    │
└───────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding basic test structure
🤔
Concept: Tests run code that checks if your app works as expected.
In Cypress, you write tests inside a describe block with it blocks for each test. Each it block contains commands to interact with the app and assertions to check results.
Result
Tests run one by one and show pass or fail results.
Knowing how tests are structured helps you see where hooks fit to run code before or after these tests.
2
FoundationWhat are hooks in Cypress
🤔
Concept: Hooks are special functions that run automatically before or after tests.
Cypress provides before, beforeEach, after, and afterEach hooks. before runs once before all tests, beforeEach runs before each test, after runs once after all tests, and afterEach runs after each test.
Result
Hooks run setup or cleanup code at the right times without repeating inside tests.
Understanding the four hook types is key to organizing test setup and teardown efficiently.
3
IntermediateUsing before and after hooks
🤔Before reading on: do you think before runs before each test or only once before all tests? Commit to your answer.
Concept: before runs once before all tests; after runs once after all tests.
Example: before(() => { cy.log('Setup once before all tests') }) after(() => { cy.log('Cleanup once after all tests') }) it('Test 1', () => { cy.log('Running test 1') }) it('Test 2', () => { cy.log('Running test 2') })
Result
Output logs show setup runs once before tests, cleanup runs once after all tests.
Knowing before and after run once helps avoid redundant setup or cleanup, saving test time.
4
IntermediateUsing beforeEach and afterEach hooks
🤔Before reading on: do you think beforeEach runs once or before every test? Commit to your answer.
Concept: beforeEach runs before every test; afterEach runs after every test.
Example: beforeEach(() => { cy.log('Setup before each test') }) afterEach(() => { cy.log('Cleanup after each test') }) it('Test 1', () => { cy.log('Running test 1') }) it('Test 2', () => { cy.log('Running test 2') })
Result
Logs show setup and cleanup run before and after each test respectively.
Using beforeEach and afterEach ensures each test starts fresh and cleans up, preventing test interference.
5
IntermediateScope of hooks in nested describes
🤔Before reading on: do hooks in a parent describe affect nested describes? Commit to your answer.
Concept: Hooks run in the scope they are defined and affect nested tests inside that scope.
Example: describe('Parent', () => { before(() => cy.log('Parent before')) describe('Child', () => { before(() => cy.log('Child before')) it('Test', () => cy.log('Test running')) }) })
Result
Output order: Parent before, Child before, Test running.
Understanding hook scope helps organize setup for groups of tests without repeating code.
6
AdvancedAvoiding common hook pitfalls
🤔Before reading on: do you think asynchronous code in hooks needs special handling? Commit to your answer.
Concept: Hooks can run asynchronous code but must return or chain Cypress commands properly to avoid timing issues.
Example of wrong async usage: before(() => { setTimeout(() => { cy.log('Delayed setup') }, 1000) }) Correct usage: before(() => { cy.wait(1000) cy.log('Proper delayed setup') })
Result
Wrong example may cause tests to start before setup finishes; correct example waits properly.
Knowing how Cypress handles async commands in hooks prevents flaky tests caused by timing errors.
7
ExpertHooks with test retries and flaky tests
🤔Before reading on: do hooks run again on test retries or only once? Commit to your answer.
Concept: Hooks like beforeEach and afterEach run on every retry, but before and after run only once per test run.
When Cypress retries a failing test, beforeEach and afterEach hooks run again to reset state. before and after hooks do not rerun, so they should not contain per-test setup.
Result
Retries properly reset test state using beforeEach/afterEach, avoiding false failures.
Understanding hook behavior with retries helps design tests that are stable and recover cleanly from failures.
Under the Hood
Cypress registers hooks as special callbacks linked to test lifecycle events. Before running tests, Cypress executes before hooks once, then beforeEach before each test, runs the test, then afterEach after each test, and finally after hooks once. Cypress commands inside hooks are queued and executed in order, ensuring proper timing and synchronization with the browser.
Why designed this way?
This design separates global setup/cleanup (before/after) from per-test setup/cleanup (beforeEach/afterEach), allowing flexible and efficient test organization. It avoids repeating expensive setup and ensures each test runs in a clean state. The command queue model ensures asynchronous browser actions run in sequence without race conditions.
┌───────────────┐
│ Test Suite    │
│ ┌───────────┐ │
│ │ before()  │ │
│ └───────────┘ │
│ ┌───────────┐ │
│ │ beforeEach│ │
│ └───────────┘ │
│ ┌───────────┐ │
│ │  Test     │ │
│ └───────────┘ │
│ ┌───────────┐ │
│ │ afterEach │ │
│ └───────────┘ │
│ ┌───────────┐ │
│ │ after()   │ │
│ └───────────┘ │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does beforeEach run once or before every test? Commit to your answer.
Common Belief:beforeEach runs only once before all tests.
Tap to reveal reality
Reality:beforeEach runs before every single test, not just once.
Why it matters:Misunderstanding this causes setup code to run too few times, leading to tests sharing state and causing flaky failures.
Quick: Do after hooks run after each test or only once after all tests? Commit to your answer.
Common Belief:after hooks run after each test.
Tap to reveal reality
Reality:after hooks run only once after all tests in the suite finish.
Why it matters:Using after for per-test cleanup causes leftover state between tests, making tests unreliable.
Quick: Can you use asynchronous code with setTimeout inside hooks safely? Commit to your answer.
Common Belief:Yes, setTimeout works fine inside hooks for async setup.
Tap to reveal reality
Reality:setTimeout does not integrate with Cypress command queue, causing hooks to finish before async code runs.
Why it matters:This leads to tests starting before setup completes, causing unpredictable failures.
Quick: Do hooks run again on test retries? Commit to your answer.
Common Belief:All hooks run again on retries.
Tap to reveal reality
Reality:beforeEach and afterEach run on retries, but before and after run only once per test run.
Why it matters:Misusing before/after for per-test setup breaks retries and causes flaky tests.
Expert Zone
1
before and after hooks are ideal for expensive setup/teardown that only needs to run once, like database seeding or server start.
2
beforeEach and afterEach hooks are crucial for isolating tests by resetting state, but overusing them can slow tests if setup is heavy.
3
Hooks run in the order they are defined and respect nesting, so ordering hooks carefully avoids unexpected side effects.
When NOT to use
Avoid using before and after hooks for per-test setup; use beforeEach and afterEach instead. For very complex setup, consider custom commands or plugins. If tests require different setups, split them into separate describe blocks with their own hooks.
Production Patterns
In real projects, before hooks often start servers or seed databases once. beforeEach resets app state or logs in users. afterEach cleans cookies or local storage. after stops servers or clears test data. Nested describes group tests with shared setup. Hooks combined with Cypress custom commands create reusable test flows.
Connections
Test Fixtures
Hooks often load fixtures before tests run.
Knowing how hooks load test data helps understand how tests get consistent inputs automatically.
Setup and Teardown in Unit Testing
Hooks in Cypress are similar to setup/teardown methods in unit test frameworks like JUnit or pytest.
Recognizing this similarity helps transfer knowledge between end-to-end and unit testing.
Cooking Preparation and Cleanup
Both involve preparing environment before main work and cleaning after to keep things tidy.
Understanding this real-world process clarifies why hooks improve test reliability and maintainability.
Common Pitfalls
#1Running asynchronous code inside hooks without Cypress commands causes timing issues.
Wrong approach:before(() => { setTimeout(() => { cy.log('Setup') }, 1000) })
Correct approach:before(() => { cy.wait(1000) cy.log('Setup') })
Root cause:setTimeout is not tracked by Cypress command queue, so hook finishes before async code runs.
#2Using before hook for per-test setup causes tests to share state.
Wrong approach:before(() => { cy.visit('/login') }) it('Test 1', () => { /* test code */ }) it('Test 2', () => { /* test code */ })
Correct approach:beforeEach(() => { cy.visit('/login') }) it('Test 1', () => { /* test code */ }) it('Test 2', () => { /* test code */ })
Root cause:before runs once, so tests run on shared page state causing flaky failures.
#3Placing afterEach hook outside describe block causes it not to run.
Wrong approach:afterEach(() => { cy.clearCookies() }) // no describe block
Correct approach:describe('Tests', () => { afterEach(() => { cy.clearCookies() }) it('Test', () => { /* test code */ }) })
Root cause:Hooks must be inside describe blocks to be registered and executed.
Key Takeaways
Before and after hooks run setup and cleanup code once per test suite, while beforeEach and afterEach run before and after every test.
Using hooks prevents repeating code inside tests, making tests cleaner, faster, and less error-prone.
Hooks run in the order and scope they are defined, affecting nested tests accordingly.
Proper handling of asynchronous code in hooks is essential to avoid flaky tests.
Understanding hook behavior with retries and nesting helps build stable and maintainable test suites.