100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Testing

Jest Interview Questions

Common Jest interview topics covering mocks, async testing, fake timers, and module mocking, with the reasoning behind each answer.

Practical JestIntermediate10 min readJul 10, 2026
Analogies

Mocks, Spies, and Stubs in Jest

Interviewers often probe whether a candidate understands that jest.fn() creates a bare mock function that records calls and can have a custom implementation, jest.spyOn() wraps an existing method on an object while preserving the original by default unless mockImplementation is called, and a stub in common usage refers to any function replacement that returns canned data regardless of input, a role jest.fn().mockReturnValue() fills. Interviewers listen for whether candidates conflate spying, which tracks a real method's usage, with fully replacing that method's behavior.

🏏

Cricket analogy: jest.spyOn wrapping a real method while still calling through is like a match referee observing Kohli's real batting technique while separately logging every shot he plays, rather than replacing his batting with a stunt double.

Testing Async Code and Timers

A frequent interview trap is forgetting to return or await a promise inside a test, which lets Jest report the test as passing before an asynchronous assertion inside a .then() callback or after an await has actually run, silently hiding a failing assertion; the fix is either returning the promise, using async/await directly, or using expect(promise).resolves/rejects. Testing rejected promises correctly requires either await expect(fn()).rejects.toThrow() or wrapping the call in a try/catch with expect.assertions(n) to guarantee the catch block's assertion actually executes.

🏏

Cricket analogy: Forgetting to await a promise in a test is like a scorer walking away from the ground before the last ball is bowled - the match result gets recorded as complete even though the actual outcome hasn't happened yet.

For code relying on setTimeout, setInterval, or debounced functions, jest.useFakeTimers() replaces the global timer implementation so calling jest.advanceTimersByTime(ms) or jest.runAllTimers() deterministically fast-forwards logical time without the test actually waiting in real wall-clock time, which is essential for testing debounce or polling logic quickly and reliably. Interviewers often ask what breaks if fake timers are enabled but never advanced, expecting the candidate to know the callback simply never fires and the test hangs or times out.

🏏

Cricket analogy: jest.advanceTimersByTime is like fast-forwarding through a rain delay on a broadcast replay instead of the viewer actually sitting through the real-time wait for the covers to come off.

Mocking Modules and Dependencies

jest.mock('./module') automatically hoists to the top of the file via Babel and replaces the module with an auto-mock unless a factory function is supplied as the second argument, and interviewers frequently check whether a candidate knows this hoisting means jest.mock calls run before imports are evaluated, which is why referencing outer-scope variables inside the factory requires prefixing them with mock, satisfying Jest's out-of-scope variable check. Manual mocks placed in a __mocks__ directory adjacent to node_modules packages or next to the source file are used automatically for node_modules packages once jest.mock('package-name') is called, but for local files a manual mock in __mocks__ still requires an explicit jest.mock() call to be applied.

🏏

Cricket analogy: jest.mock hoisting above imports is like the toss happening before the players even walk onto the field - the decision is locked in before anything else in the sequence occurs.

javascript
// debounce.test.js
jest.useFakeTimers();
import { debouncedSave } from './autosave';
import { api } from './api';

jest.mock('./api');

test('debouncedSave only calls the API once after the delay', () => {
  debouncedSave('draft 1');
  debouncedSave('draft 2');
  debouncedSave('draft 3');

  expect(api.save).not.toHaveBeenCalled();

  jest.advanceTimersByTime(500);

  expect(api.save).toHaveBeenCalledTimes(1);
  expect(api.save).toHaveBeenCalledWith('draft 3');
});

test('rejected promise is handled', async () => {
  api.save.mockRejectedValueOnce(new Error('network error'));
  await expect(api.save('x')).rejects.toThrow('network error');
});

A strong interview answer distinguishes the three terms precisely: a mock records and controls behavior, a spy watches a real implementation while optionally still calling through, and a stub is any replacement that returns canned output regardless of input - Jest's jest.fn() and jest.spyOn() can implement all three depending on configuration.

A classic interview gotcha: forgetting jest.useRealTimers() in an afterEach after enabling fake timers in one test file leaks fake timers into subsequent tests, causing unrelated setTimeout-based code elsewhere to hang or behave unexpectedly.

  • jest.fn() creates a bare trackable mock; jest.spyOn() wraps a real method by default.
  • Always return, await, or use resolves/rejects for promise-based assertions in tests.
  • expect.assertions(n) guarantees a try/catch assertion actually ran during async tests.
  • jest.useFakeTimers() plus advanceTimersByTime lets you test debounce/timeout logic instantly.
  • jest.mock() calls are hoisted above imports by Babel, before module evaluation.
  • Factory functions passed to jest.mock() must reference mock-prefixed outer variables only.
  • Reset real timers with jest.useRealTimers() in afterEach to avoid leaking state across tests.

Practice what you learned

Was this page helpful?

Topics covered

#Testing#JestStudyNotes#TestingQA#JestInterviewQuestions#Jest#Interview#Questions#Mocks#StudyNotes#SkillVeris