Testing TypeScript

Automated tests prove your code does what you think — and keep proving it as you change things. You'll learn the describe / it / expect pattern with Vitest and Jest , the everyday matchers, how to mock and type dependencies, and how to test async code and read coverage.

Learn Testing TypeScript in our free TypeScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Tests are the smoke detectors of your codebase. You don't notice them while everything works, but the moment a change starts a fire — a function quietly returning the wrong value — they go off immediately, pointing at the exact room. Without them you only find out when the whole house is ablaze in production.

1. What a TypeScript Test File Looks Like

A test file imports the runner's helpers, imports the code under test, and asserts on its behaviour. With Vitest this is a plain .test.ts file that runs directly — esbuild transforms the TypeScript for you. Read the real version here:

2. describe / it / expect

describe groups related tests, it (or test ) declares one case, and expect(value) wraps the value you want to assert on. The box below provides tiny stand-ins so you can see the whole flow run.

3. Matchers: toBe vs toEqual

A matcher says how to compare. Use .toBe() for primitives (strict equality) and .toEqual() for objects and arrays (deep, value-based equality). .toThrow() asserts a function throws. Run the example to see why objects need toEqual :

4. Mocks & Async Tests

A mock is a fake function that records its calls so you can assert on them — vi.fn() in Vitest, jest.fn() in Jest. Typing the mock lets the compiler check it matches the real signature. For async code, make the test async and await the result so the runner waits.

Your turn. Fill in the two blanks marked ___ to complete the assertions, then run it.

No blanks this time — write the three assertions yourself using the provided expect , run it, and check all three print pass.

Practice quiz

What is the role of describe() in a test file?

  • It groups related tests under a shared label
  • It asserts a value
  • It mocks a function
  • It runs the production code

Answer: It groups related tests under a shared label. describe(label, fn) groups related test cases so output is organised and shared setup can live in one place.

Which function defines a single test case?

  • assert
  • describe
  • it (or test)
  • expect

Answer: it (or test). it(name, fn), with its alias test(name, fn), declares one individual test case.

What does expect(sum(2, 3)).toBe(5) check?

  • That the test is async
  • That sum(2, 3) is strictly equal to 5
  • That sum throws
  • That sum is mocked

Answer: That sum(2, 3) is strictly equal to 5. expect(value) wraps the actual value and .toBe(5) asserts strict (Object.is) equality with 5.

Which matcher compares objects and arrays by value (deep equality)?

  • .toContain()
  • .toThrow()
  • .toBe()
  • .toEqual()

Answer: .toEqual(). .toEqual() recursively compares structure and values; .toBe() would fail for two different object references.

In Vitest, how do you create a mock function?

  • vi.fn()
  • expect.fn()
  • mock.create()
  • new Mock()

Answer: vi.fn(). vi.fn() creates a mock function in Vitest (jest.fn() is the Jest equivalent) that records its calls.

How do you assert a mock was called with specific arguments?

  • expect(fn).toBe(arg)
  • expect(fn).toHaveBeenCalledWith(arg)
  • expect(fn).called(arg)
  • fn.assert(arg)

Answer: expect(fn).toHaveBeenCalledWith(arg). .toHaveBeenCalledWith(...) checks the mock received exactly those arguments on at least one call.

What is the correct way to test code that returns a Promise?

  • Wrap it in describe
  • Ignore the Promise
  • Use a setTimeout
  • Make the test async and await the result

Answer: Make the test async and await the result. Declare the test callback async and await the promise, then assert on the resolved value so the runner waits for it.

How does Vitest support TypeScript test files out of the box?

  • It compiles with the C++ compiler
  • It requires Babel only
  • It uses esbuild to transform TS, so .ts/.tsx tests run with no extra config
  • It cannot run TypeScript

Answer: It uses esbuild to transform TS, so .ts/.tsx tests run with no extra config. Vitest transforms TypeScript with esbuild internally, so .test.ts files run directly. With Jest you typically add ts-jest.

What does code coverage measure?

  • How fast tests run
  • How many lines/branches of code the tests exercised
  • How many files exist
  • The TypeScript version

Answer: How many lines/branches of code the tests exercised. Coverage reports the percentage of statements, branches, and functions executed while the tests ran.

Why does typing a mock (for example a typed vi.fn()) help?

  • It lets the compiler check that the mock matches the real function's signature
  • It disables type checking
  • It makes tests run faster
  • It changes the runtime behaviour

Answer: It lets the compiler check that the mock matches the real function's signature. A typed mock must match the real signature, so the compiler catches a mock that returns the wrong shape or takes wrong args.