Introduction
Welcome again to the second part of the Mastering Jest blog. This is the second part of my mastering jest blog. If you haven’t gone through the first part then here’s the link. I am really excited about this blog because we will get to learn a lot in this blog—topics such as setup, Mock functions, Order or execution, and some best practices.
Setup and Teardown
beforeEach
: Runs before each TestafterEach
: Runs after each TestbeforeAll
: Runs only once at startafterAll
: Runs only once at the end
If the setup process is resource-intensive or time-consuming, consider using
beforeAll
instead ofbeforeEach
.beforeAll
sets up the environment once for the entire test suite, reducing overhead for each individual test case.
Best practice for Setup and Teardown
In Jest, beforeAll
and afterAll
are global setup and teardown functions, respectively. They allow you to perform setup tasks before running any test cases (for beforeAll
) and cleanup tasks after running all test cases (for afterAll
). These functions are particularly useful when you need to set up common resources or configurations for multiple tests or when you want to clean up any changes made during testing.
Let's assume you have a simple application that fetches data from an API, and you want to test the functionality of the API client. In this example, we'll use beforeAll
to set up the API client and afterAll
to clean up any resources after all tests are executed.
const ApiClient = require('../apiClient');
let apiClient;
beforeAll(() => {
// This runs once before all test cases
apiClient = new ApiClient();
});
afterAll(() => {
// This runs once after all test cases are done
apiClient = null; // Clean up the API client instance
});
test('should fetch data from the API', async () => {
// Arrange
const url = '<https://jsonplaceholder.typicode.com/todos/1>';
// Act
const data = await apiClient.fetchData(url);
// Assert
expect(data.userId).toBe(1);
expect(data.id).toBe(1);
expect(data.title).toBeTruthy();
expect(data.completed).toBeDefined();
});
test('should handle API errors', async () => {
// Arrange
const invalidUrl = '<https://jsonplaceholder.typicode.com/invalid>';
// Act and Assert
await expect(apiClient.fetchData(invalidUrl)).rejects.toThrow();
});
In this example, we use beforeAll
to instantiate the ApiClient
once before running any test cases. This ensures that the API client is set up and ready to be used by all test cases.
We also use afterAll
to clean up after all the tests are done. In this case, we set the apiClient
variable to null
to release any resources held by the API client instance.
Scoping
describe
: Use describe block to separate your test cases
The hooks declared inside a
describe
block apply only to the tests within thatdescribe
block
Order of Execution
Jest executes all describe handlers in a test file before it executes any of the actual tests
This is another reason to do setup and teardown inside
before*
andafter*
handlers rather than inside thedescribe
blocksOnce the
describe
blocks are complete, by default Jest runs all the tests serially in the order they were encountered in the collection phase
describe('describe outer', () => {
console.log('describe outer-a');
describe('describe inner 1', () => {
console.log('describe inner 1');
test('test 1', () => console.log('test 1'));
});
console.log('describe outer-b');
test('test 2', () => console.log('test 2'));
describe('describe inner 2', () => {
console.log('describe inner 2');
test('test 3', () => console.log('test 3'));
});
console.log('describe outer-c');
});
// describe outer-a
// describe inner 1
// describe outer-b
// describe inner 2
// describe outer-c
// test 1
// test 2
// test 3
Mock Functions
Mock functions in Jest are a powerful feature that allows you to create and control "fake" implementations of functions, methods, or modules during testing
You can create a mock function using
jest.fn()
orjest.mock()
:
// Standalone mock function
const mockFunction = jest.fn();
- You can configure the behavior of mock functions using Jest matchers such as
mockReturnValue
,mockResolvedValue
,mockRejectedValue
, and more.
const mockFunction = jest.fn();
mockFunction.mockReturnValue(42); // Always return 42 when called
// Return a resolved promise with the specified data
mockFunction.mockResolvedValue({ data: 'mocked data' });
Asserting Mock Function Calls:
You can use matchers like toHaveBeenCalled
, toHaveBeenCalledTimes
, toHaveBeenCalledWith
, and others to verify if and how the mock function was called during the test.
const mockFunction = jest.fn();
// Call the function
mockFunction('param');
// Assertions
expect(mockFunction).toHaveBeenCalled();
expect(mockFunction).toHaveBeenCalledTimes(1);
expect(mockFunction).toHaveBeenCalledWith('param');
Reset Mock Functions
Jest automatically tracks the usage of mock functions during test runs. After each test, the mock functions are reset, clearing any calls, return values, or other configurations.
const mockFunction = jest.fn();
// Call the function
mockFunction('param');
// Reset the mock function
mockFunction.mockReset();
Jest Best Practices
Isolate Tests: Ensure each test case is independent and does not rely on the state of other tests. Isolation avoids interference and provides accurate results.
Descriptive Test Names: Use meaningful and descriptive test names to make it clear what each test is validating.
Arrange-Act-Assert Pattern: Follow the AAA pattern to structure your tests: Arrange the test setup, Act on the code being tested, and Assert the expected outcome.
Use Matchers: Utilize Jest's matchers for expressive assertions, improving test readability.
Mock External Dependencies: Mock external services or complex functions to isolate code during testing.
Snapshot Testing for UI Components: Use snapshot testing to ensure the output of UI components remains consistent over time.
Test Coverage: Aim for good test coverage to ensure comprehensive testing of your code.
Use async/await: Handle asynchronous code gracefully using async/await or return promises in tests.
Before and After Hooks: Use
beforeEach
andafterEach
for common setup and teardown tasks.beforeAll and afterAll: Use
beforeAll
andafterAll
for global setup and cleanup tasks.Test Organization: Group related tests using
describe
blocks to improve test organization.Clear Side Effects: Clean up any side effects or global state changes introduced during tests.
Continuous Integration: Set up automated testing with Jest in your CI/CD pipeline.
Keep Tests Fast: Write efficient tests to maintain fast test execution times.
Update Snapshots: Regularly update snapshots to reflect intentional changes in the output of components.
By following these best practices, you can create well-structured, reliable, and maintainable test suites, enabling you to confidently ship high-quality code with Jest.
We have arrived at the end of this blog, I have covered all the important topics for Jest testing framework. I tried to cover almost everything but still, there is lot to learn. I will keep coming with new blogs so stay tuned. Thank you for reading this blog.