Skip to content

describe, it, and test

describe(), it(), and test() are the three functions you will use most often in Jest. This lesson explains what each one does, how they relate, and how to structure them for readable test output.

Grouping Tests with describe()

describe(name, fn) creates a labeled block that groups related tests together. It does not run any assertions itself — it is purely organizational, and its name shows up as a heading in Jest's output, nested above the individual tests inside it.

Inside a describe() block, you define individual test cases with test() or its alias it(). Both take a description and a callback function; the description should read like a sentence describing the expected behavior.

describe('formatCurrency', () => {
  it('formats whole numbers with two decimal places', () => {
    expect(formatCurrency(9)).toBe('$9.00');
  });

  it('rounds to two decimal places', () => {
    expect(formatCurrency(9.999)).toBe('$10.00');
  });
});

The describe() label plus each it() description reads naturally: "formatCurrency formats whole numbers with two decimal places."

Nesting describe() Blocks

describe('UserService', () => {
  describe('createUser', () => {
    it('throws when the email is invalid', () => { /* ... */ });
  });

  describe('deleteUser', () => {
    it('removes the user from the database', () => { /* ... */ });
  });
});
  • describe() blocks can be nested to represent sub-features or methods.
  • Each nested level adds indentation in Jest's terminal output, mirroring your code structure.
  • test() and it() are interchangeable; some teams use test() at the top level and it() inside describe().
  • Avoid putting expect() calls directly inside describe() — they belong inside test()/it().

describe/it/test Cheat Sheet

Quick reference for structuring test files.

Function Purpose
describe(name, fn) Groups related tests under a shared label
test(name, fn) Defines a single test case
it(name, fn) Alias for test()
test.only(...) Runs only this test, skipping others in the file
test.skip(...) Skips this test without deleting it
test.todo(...) Marks a test as planned but not yet written
describe.each([...]) Runs a describe block once per data row

test.only, test.skip, and test.todo

While debugging, test.only() runs a single test and skips every other test in the file, which is useful for isolating a failure quickly. test.skip() does the opposite, marking a test as intentionally not run (for example, a known-broken test you haven't fixed yet). test.todo() documents a planned test without an implementation, which shows up in output as a reminder.

test.only('runs only this test', () => {
  expect(1 + 1).toBe(2);
});

test.skip('temporarily disabled test', () => {
  expect(brokenFeature()).toBe(true);
});

test.todo('should validate email format');

Remove .only() before committing — it's easy to accidentally leave a suite half-skipped.

Writing Descriptive Test Names

A well-named test describes behavior, not implementation. "returns null for an empty array" is far more useful in a failure report than "test 1" or "works correctly", because the failing description alone tells you what broke without opening the file.

  • Bad: it('works', () => {})
  • Good: it('returns null when the input array is empty', () => {})
  • Bad: it('test error case', () => {})
  • Good: it('throws a TypeError when id is not a number', () => {})

Common Mistakes

  • Putting expect() assertions directly inside a describe() callback instead of inside test()/it().
  • Leaving test.only() in committed code, silently disabling the rest of the suite.
  • Writing vague test names like "works" that give no information when a test fails.
  • Over-nesting describe() blocks many levels deep, making output hard to scan.

Key Takeaways

  • describe() groups tests; it does not itself make assertions.
  • test() and it() define individual test cases and are functionally identical.
  • .only(), .skip(), and .todo() give fine-grained control over which tests run.
  • Descriptive test names make failure reports useful without opening the test file.

Pro Tip

Read your test names out loud as a sentence: "UserService createUser throws when the email is invalid." If it doesn't read naturally, rename it before moving on.