Skip to content

describe and it in Cypress

Cypress uses Mocha's describe/it syntax to structure tests. This lesson explains each block's role and shows patterns for keeping test files readable as they grow.

describe and it Explained

describe() creates a named grouping of related tests; it accepts a description string and a callback function. it() defines a single test case inside a describe() block, also with a description and a callback containing the actual test steps.

These blocks can be nested: an outer describe() for a feature area, with inner describe() blocks for more specific scenarios, each containing their own it() test cases.

describe('Shopping cart', () => {
  describe('when the cart is empty', () => {
    it('shows an empty state message', () => {
      cy.visit('/cart');
      cy.get('[data-cy="empty-cart"]').should('be.visible');
    });
  });
});

Nested describe() blocks read almost like a spec document: "Shopping cart, when the cart is empty, shows an empty state message."

Available Lifecycle Hooks

before(() => { /* runs once, before all tests in this block */ });
beforeEach(() => { /* runs before every test in this block */ });
afterEach(() => { /* runs after every test in this block */ });
after(() => { /* runs once, after all tests in this block */ });
  • beforeEach() is the most commonly used hook, typically for visiting a page or resetting state.
  • before() runs only once and should be used sparingly, since Cypress resets state between tests by design.
  • context() is an alias for describe(), purely for readability in some codebases.
  • Hooks cascade: an outer beforeEach() runs before an inner one, in the order they are declared.

describe/it Quick Reference

The core Mocha-style structural blocks used throughout Cypress test suites.

Block Purpose
describe('name', fn) Group related tests
context('name', fn) Alias for describe()
it('name', fn) Define a single test
it.only('name', fn) Run only this test in the file
it.skip('name', fn) Skip this test
beforeEach(fn) Run before every test in this block
afterEach(fn) Run after every test in this block

Using .only and .skip While Developing

While writing or debugging a specific test, it.only() restricts a run to just that test, skipping every other it() in the file, without needing to comment anything out. it.skip() does the opposite, temporarily excluding a test from running.

Both are meant as temporary tools during development, leaving .only() in committed code will silently prevent other tests in that file from ever running in CI.

describe('Checkout', () => {
  it.only('applies a valid discount code', () => {
    // only this test runs while you focus on it
  });

  it('rejects an invalid discount code', () => {
    // skipped while .only() is present above
  });
});

Keeping beforeEach Hooks Focused

A beforeEach() hook should set up only what every test in that block genuinely needs. Overloaded hooks that visit multiple pages or seed unrelated data make individual test failures harder to diagnose, since it becomes unclear which setup step actually failed.

  • Keep beforeEach() scoped to the describe block it lives in, not the whole file.
  • Prefer several nested describe() blocks with focused hooks over one giant shared hook.
  • Move truly one-off setup into the specific it() that needs it instead of a shared hook.

Common Mistakes

  • Committing code with it.only() still present, silently disabling other tests in CI.
  • Writing vague test descriptions like it('works') instead of describing the specific expected behavior.
  • Overloading a single beforeEach() with unrelated setup for many different tests.
  • Nesting describe() blocks so deeply that the structure becomes harder to read than a flat list would be.

Key Takeaways

  • describe() groups tests; it() defines an individual test case within a group.
  • Lifecycle hooks (before, beforeEach, afterEach, after) manage setup and teardown around tests.
  • .only and .skip are useful during development but should not be committed.
  • Keep beforeEach() hooks focused on what the enclosing block's tests actually need.

Pro Tip

Write your it() description as a complete sentence describing behavior, not implementation, for example "shows an error when the password is too short" reads far better in CI output and reports than "tests password validation."