This a repository about learning to write Unit Tests in React.js with TypeScript. The tutorial and codebase were created by TrungQuanDev.
* nodejs >= v22.12.2
* npm >= v10.5.0
* yarn >= v1.22.19
- Test File Naming:
*.test.ts
/*.test.tsx
- Standard convention*.spec.ts
/*.spec.tsx
- Alternative convention- Place alongside source files or in
__tests__
folder
globalThis
: global variable (ES2020)
Jest provides global functions (
describe
,it
,expect
, etc.) - no imports needed.
-
Lifecycle hooks
beforeAll(fn, timeout)
- Runs once before all tests in the fileafterAll(fn, timeout)
- Runs once after all tests in the filebeforeEach(fn, timeout)
- Runs before each testafterEach(fn, timeout)
- Runs after each testtimeout
: optional, default 5s (milliseconds)- All wait for promises/generators to resolve
-
describe(name, fn)
- Groups related tests into a block
- Optional but recommended
- Can nest for test hierarchies
-
test(name, fn, timeout)
/it(name, fn, timeout)
- Runs a test
timeout
: Optional, default 5s (milliseconds)
-
test.each(table)(name, fn, timeout)
/it.each(table)(name, fn, timeout)
- Run the same test with different data
table
: Array of Arrays with test argumentsname
: Supports printf formatting for parameter injection (%s
,%d
,%i
,%f
,%j
,%o
,%#
)fn
: Receives each row as arguments
Used to test values with "matcher" functions
- Provides assertions like
toBe()
,toEqual()
,toContain()
, etc.
Mock functions (also called "spies") track function calls and control behavior
- Creating Mocks: e.g.
jest.fn()
,jest.fn()
, ... - Return Values: e.g.
mockFn.mockResolvedValueOnce(value)
,mockFn.mockRejectedValueOnce(value)
,mockFn.mockImplementation()
, ... - Reset/Restore: e.g.
mockFn.mockReset()
,mockFn.mockRestore()
, ... - Assertions: e.g.
.toHaveBeenCalled()
,.toHaveBeenCalledTimes()
,.toHaveBeenCalledWith()
, ...
The
jest
object is automatically in scope within every test file. It provides methods to create mocks and control Jest's behavior.
-
Mock Functions
jest.fn()
: Returns a new, unused mock functionjest.spyOn(object, methodName)
:- Creates a mock function similar to
jest.fn()
but also tracks calls toobject[methodName]
. - Returns a Jest mock function.
- Creates a mock function similar to
jest.clearAllMocks()
- Clear all mock history (calls, instances, results).
- Equivalent to calling
.mockClear()
on every mocked function.
jest.restoreAllMocks()
- Restore all mocks to original values.
- Equivalent to calling
.mockRestore()
on every mocked function and.restore()
on every replaced property.
jest.resetAllMocks()
- Reset all mocks (clears history and implementations).
- Equivalent to calling
.mockReset()
on every mocked function.
-
Module Mocking
jest.resetModules()
- Reset module registry cache.
- Useful to isolate modules where local state might conflict between tests.
-
Fake Timers
jest.useFakeTimers()
- Fake timers for all tests in file until restored with
jest.useRealTimers()
. - This is a global operation affecting other tests in the same file.
- Fake timers for all tests in file until restored with
jest.useRealTimers()
- Restore original implementations of global date, performance, time and timer APIs
jest.runOnlyPendingTimers()
- Executes only macro-tasks currently pending (queued by
setTimeout()
orsetInterval()
). - New tasks scheduled by pending tasks won't execute.
- Executes only macro-tasks currently pending (queued by
jest.advanceTimersByTime(msToRun)
- Fast-forward time by specified milliseconds.
- Executes all macro-tasks queued by
setTimeout()
,setInterval()
, andsetImmediate()
.
-
screen
- Pre-bound to
document.body
with all query methods - Access rendered elements
- Pre-bound to
-
Types of Queries
- Single:
getBy...
,queryBy...
,findBy...
- Multiple:
getAllBy...
,queryAllBy...
,findAllBy...
- Single:
-
When to use
getBy...
- Element expected (throws if not found)queryBy...
- Element may not exist (returnsnull
)findBy...
- Element appears asynchronously (returnsPromise
, default 1000ms timeout)
-
Priority Order (most accessible → least)
- Queries Accessible to Everyone
getByRole
(best - includes accessibility)getByLabelText
,getByPlaceholderText
,getByText
,getByDisplayValue
, etc.
- Semantic Queries
getByAltText
,getByTitle
- Test IDs
getByTestId
- Queries Accessible to Everyone
- Async Methods
findBy
queries are a combination ofgetBy
queries andwaitFor
- Usage: Expect an element to appear but the DOM change might not happen immediately (e.g., API calls,
setTimeout
, animations) - Can specify custom timeout:
findByText('text', {}, { timeout: 3000 })
- Usage: Expect an element to appear but the DOM change might not happen immediately (e.g., API calls,
userEvent
: Simulates real browser user interactions (preferred overfireEvent
for more realistic behavior).
-
Setup
userEvent.setup()
- Initialize user instance (required before interactions)
-
Common Interactions
user.click(element)
- Click elementuser.type(element, text, options?)
- Type text into input- ...etc
Testing Library: React Testing
-
render(component)
- Mounts component into virtual DOM for testing
- Returns queries bound to container and utility functions
-
act(callback)
:- Light wrapper around React's
act
function - Forwards all arguments to
act
if React version supports it - All it does is forward all arguments to the act function if your version of react supports act
- Light wrapper around React's
-
renderHook(callback, option?)
- Convenience wrapper around
render
with custom test component - Used for testing custom hooks in isolation
- Convenience wrapper around
Target Coverage: Aim for 80%+ coverage, but focus on testing critical paths and edge cases rather than chasing 100%.
Metric | Name | Meaning |
---|---|---|
% Stmts |
Statements | Percentage of executable statements covered |
% Branch |
Branches | Percentage of code branches executed (if-else, switch-case) |
% Funcs |
Functions | Percentage of functions called at least once |
% Lines |
Lines | Percentage of source code lines executed |
-
Standard structure for organizing unit tests:
- Arrange - Setup: mock data, dependencies, render components
- Act - Execute: perform the action being tested
- Assert - Verify: check expected outcomes with matchers
-
Example:
// Example: Complete AAA pattern it('submits form with user data', async () => { // Arrange const user = userEvent.setup(); const mockSubmit = jest.fn(); render(<LoginForm onSubmit={mockSubmit} />); // Act await user.type(screen.getByLabelText(/email/i), 'user@example.com'); await user.type(screen.getByLabelText(/password/i), 'password123'); await user.click(screen.getByRole('button', { name: /submit/i })); // Assert expect(mockSubmit).toHaveBeenCalledTimes(1); expect(mockSubmit).toHaveBeenCalledWith({ email: 'user@example.com', password: 'password123', }); });
- TrungQuanDev
- Khang Nguyen