Jest parameterized tests allow you to define the test logic once and run it against multiple sets of inputs and expected results.
This guide explains how Jest parameterized tests work in Jest, when to use functions like test. each() and describe. each(), how to structure test data, and how to use parameterized tests as part of a broader browser testing strategy.
Read More: How to Debug Jest Tests
What are Parameterized Tests in Jest?
Parameterized tests in Jest allow you to run the same test with different sets of input values and expected outcomes on a web app.
This helps in reducing repetitive code, making your tests more concise and maintainable.
Suppose you have a function that adds two numbers. You want to check several combinations:
- 1 + 2 should return 3
- 2 + 3 should return 5
- 5 + 7 should return 12
A straightforward approach is to create three tests:
test('adds 1 + 2 to equal 3', () => {
expect(1 + 2). toBe(3);
});
test('adds 2 + 3 to equal 5', () => {
expect(2 + 3). toBe(5);
});
test('adds 5 + 7 to equal 12', () => {
expect(5 + 7). toBe(12);
});The tests work, but the structure is almost identical. With a parameterized test, you can separate the test logic from the test data:
test.each([
[1, 2, 3],
[2, 3, 5],
[5, 7, 12],
])('adds %i + %i to equal %i', (a, b, expected) => {
expect(a + b). toBe(expected);
});Here, Jest runs the same test for each row in the table. That distinction is the main idea behind parameterized testing:
One test → multiple data sets → multiple test cases.
Parameterized tests are useful when the assertion and test script stay largely the same while the input values change.
How Parameterized Tests in Jest Work?
In Jest, parameterized tests are implemented using the .each() method, which allows you to supply a table of inputs and expected outputs to a single test. Jest then iterates through each set of parameters and runs the test for each one.
The most common ones are
- test.each() runs one test with multiple data sets.
- it.each() is an alias for test.each().
- describe.each() runs a group of tests against multiple data sets.
Syntax for Parameterized Tests
test.each(table)(name, fn);
For example:
test.each([
[10, 5, 15],
[20, 10, 30],
[7, 3, 10],
])('adds %i + %i to equal %i', (a, b, expected) => {
expect(a + b). toBe(expected);
});Explanation:
- Each subarray ([10,5,15], etc.) represents a set of inputs (a, b) and the expected output.
- The test message (adds %i + %i to equal %i) is dynamically updated with the values for each test.
These values change from one test case to another. Jest passes each row to the test function and creates an individual test case and test suite from it.
The basic syntax is
test.each(table)(name, fn);
Jest’s parameterized APIs support formatting values in generated test names, which makes individual cases easier to identify when a test fails.
When should you use Jest Parameterized Tests?
Jest parameterised tests are ideal for situations where you want to test a single function or piece of code with multiple inputs and expected outputs.
Here are some specific scenarios:
1. Testing Functions with Multiple Input/ Output Combinations:
- Mathematical Operations: Testing functions like add, subtract, multiply, and divide with various input values.
- String Manipulation: Testing functions like trim, toUpperCase, and toLowerCase with different strings.
- Array Operations: Testing functions like map, filter, and reduce with various array inputs.
For a function that accepts a percentage, you might want to test values such as 0, 1, 99, and 100.
test.each([
[0, false],
[1, true],
[99, true],
[100, true],
])('isValidPercentage(%i) returns %s', (value, expected) => {
expect(isValidPercentage(value)).toBe(expected);
});2. Testing Edge Cases and Boundary Conditions:
- Invalid Input: Testing how your function handles invalid or unexpected input values.
- Empty Input: Testing how your function behaves with empty input.
- Large Input: Testing how your function performs with large input datasets.
test.each([
['user@example.com', true],
['name@example', false],
['', false],
['user@.com', false],
])('validates email %s as %s', (email, expected) => {
expect(isValidEmail(email)).toBe(expected);
});3. Testing different data types
- Numbers: Testing with integers, floats, and other numeric types.
- Strings: Testing with different character encodings and special characters.
- Objects and Arrays: Testing with various object structures and array lengths.
4. Testing Multiple Scenarios within a Single Test
- Different User Roles: Testing how your application behaves for different user roles.
- Multiple Language Support: Testing how your application handles different languages.
- Different Browser Compatibility: Testing how your application works in different browsers.
- Account Types: Different user tiers like free, premium, or enterprise are used to verify feature access and billing-related behavior.
- API Response States: Different backend outcomes, such as success, error, or loading states, are used to test how the UI handles server responses.
- Product Categories: Groupings of items in an application (e.g., electronics, clothing) used to validate filtering, listing, and categorization logic.
- Permissions: Access control rules that determine what actions a user can perform within the system based on their role or privileges.
5. Reduce Test Code Redundancy
- Common Test Logic: If you have multiple tests with similar logic, parameterizing them can reduce code duplication.
- Data-Driven Testing: By parameterizing your tests, you can easily change the test data without modifying the test logic.
That said, if two tests require substantially different setups, assertions, or behavior, forcing them into one table can make the test harder to understand.
The primary question to ask yourself is, do I have to test for the same web action with different values, like the login form? Or do I need several tests to run for each data set, like testing login for multiple users (guest, admin, customer) that leads to different profiles?
Jest Parameterized Testing Types
Jest supports various types of parameterized testing, enabling you to test multiple scenarios with different input combinations efficiently. Here are some examples:
Using functions like test.each() or describe. each() depends on whether you are repeating one test or running several tests.
- Use test.each() when the same individual test needs to run with different data.
- Use describe. each() when you check several properties for each user type
1. Using test.each()
const users = [
['Alice', 'admin'],
['Bob', 'editor'],
['Charlie', 'viewer'],
];
test.each(users)('%s has the role %s', (name, role) => {
expect(getUserRole(name)).toBe(role);
});2. Using describe.each()
const users = [
{ role: 'admin', canEdit: true, canDelete: true },
{ role: 'editor', canEdit: true, canDelete: false },
{ role: 'viewer', canEdit: false, canDelete: false },
];
Describe each user (users) ( '$role permissions', ({ role, canEdit, canDelete }) => {
test('has the expected edit permission', () => {
expect(canEditPermission(role)).toBe(canEdit);
});
test('has the expected delete permission', () => {
expect(canDeletePermission(role)).toBe(canDelete);
});
});Here, describe. Each() is useful because the same group of assertions is repeated for every row.
3. Asynchronous Parameterized Tests
Asynchronous parameterized tests allow you to run the same test logic with different input data sets, where the test logic may involve asynchronous operations.
This can be particularly useful for ensuring your code works across a variety of scenarios while leveraging asynchronous features like promises or async/await.
How to Implement Asynchronous Parameterized Tests in Jest
Use async/await inside the test function to handle asynchronous operations.
Example
const fetchData = async (input) => {
// Simulates an async operation
return new Promise((resolve) => {
setTimeout(() => resolve(`Processed: ${input}`), 100);
});
};
describe('Asynchronous Parameterized Tests', () => {
const testCases = [
['Input1', 'Processed: Input1'],
['Input2', 'Processed: Input2'],
['Input3', 'Processed: Input3'],
];
test.each(testCases)(
'fetchData(%s) resolves to %s',
async (input, expectedOutput) => {
const result = await fetchData(input);
expect(result).toBe(expectedOutput);
}
);
});Read More: it.each Function in Jest
4. Parameterized Table-Driven Tests
Parameterized (or table-driven) tests in Jest allow you to define a table of input data and expected outcomes for a test, enabling you to run the same logic across multiple scenarios.
Jest provides methods like test.each() or it.each() to implement this pattern effectively. Basics of Table-Driven Tests in Jest
- test.each or it. each:
- Used for defining table-driven tests.
- Accepts an array of data (a “table”).
- Each row in the array is passed as individual arguments to the test function.
- Table Formats:
- Inline arrays: Define the table directly as an array of arrays.
- Tagged templates: Use template literals for better readability.
//Example:
describe('Table-Driven Tests - Inline Table', () => {
test.each([
[1, 2, 3], // [a, b, expected]
[5, 5, 10],
[2, 3, 5],
])('adds %i + %i to equal %i', (a, b, expected) => {
expect(a + b). toBe(expected);
});
});- Each array in the table represents a single test case.
- The variables a, b, and expected correspond to the values in each row.
Read More: How to Configure Jest
5. Parameterized Snapshot Tests
Parameterized snapshot tests in Jest allow you to dynamically test multiple input-output scenarios with Jest’s built-in snapshot testing functionality.
This is particularly useful when you want to verify that a component is used by multiple users and leads to different pages.
Setting Up Parameterized Snapshot Tests
You can use a test. each of them. each() function to define a table of input-output cases and generate snapshots for each one.
Example:
const formatData = (data) => {
return { ...data, formatted: true };
};
describe('Parameterized Snapshot Tests - Function Output', () => {
test.each([
{ input: { name: 'Alice' }, description: 'with name Alice' },
{ input: { name: 'Bob' }, description: 'with name Bob' },
{ input: { age: 30 }, description: 'with age 30' },
])('formats data $description', ({ input }) => {
const result = formatData(input);
expect(result).toMatchSnapshot();
});
});
Here,
- The test.each runs the snapshot test for each input in the array.
- Snapshots are saved separately for each test case.
- Descriptions in the table make snapshots easier to identify.
6. Nested Parameterized Tests
Nested parameterized tests in Jest allow you to test combinations of inputs by nesting tests. each (or it.each) within a describe. each other’s test.
This is particularly useful when you want to test a set of dependent parameters or multiple layers of variability systematically.
Structure of Nested Parameterized Tests
- Outer loop: Defines a higher level of variation (for example, different test categories or contexts).
- Inner loop: Defines specific cases or scenarios for each category.
Example:
const add = (a, b) => a + b;
describe.each([
['positive numbers', [1, 2], [3, 4], [5, 6]],
['negative numbers', [-1, -2], [-3, -4], [-5, -6]],
['mixed numbers', [1, -2], [-3, 4], [5, -6]],
])('Addition with %s', (description, ...cases) => {
test.each(cases)('add(%i, %i) equals %i', (a, b) => {
expect(add(a, b)). toBe(a + b);
});
});- The outer loop (describe.each) iterates over the categories of numbers.
- The inner loop (test.each) iterates over individual test cases for each category.
Read More: Understanding Jest BeforeEach function
How to Structure Test Data in Jest
Jest supports various formats for test data inputs, making it flexible and easy to organize tests for different scenarios.
1. Inline Data
- Simple Arrays: Use inline arrays for straightforward test cases.
test('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});- Object Literals: Test specific object properties inline.
test('object assignment', () => {
const data = { one: 1, two: 2 };
expect(data.one).toBe(1);
expect(data.two).toBe(2);
});2. Data-Driven Testing with it.each
- Array of Arrays: Organize test cases in a table format for multiple scenarios.
const testCases = [
[1, 2, 3],
[4, 5, 9],
[6, 7, 13],
];
it.each(testCases)('adds %i + %i to equal %i', (a, b, expected) => {
expect(a + b).toBe(expected);
});- Array of Objects: Use destructuring to improve readability.
const testCases = [
{ a: 1, b: 2, expected: 3 },
{ a: 4, b: 5, expected: 9 },
{ a: 6, b: 7, expected: 13 },
];
it.each(testCases)('adds $a + $b to equal $expected', ({ a, b, expected }) => {
expect(a + b).toBe(expected);
});3. External Data Files
- JSON: Import JSON files directly for test data.
const testData = require('./testData.json');
it.each(testData)('tests with data from JSON', (test) => {
// ...
});- CSV: You can use libraries like csv-parser to parse CSV files and use the parsed data in your tests.
Read More: How to Test React App using Jest
Primitive Data Types in Jest
Jest allows you to test primitive types (like string, number, boolean, null, undefined, symbol, and bigint) in various ways. These are some scenarios and examples of how you can effectively test primitive values in Jest:
1. Testing Numbers
Basic Equality: Use toBe for exact comparisons or toEqual (though toBe is sufficient for primitives).
test('numbers match', () => {
expect(2 + 2).toBe(4);
expect(3.14).toEqual(3.14);
});2. Testing Strings
String Comparisons: Use toBe for exact matches or toMatch for patterns using regular expressions.
test('string equality', () => {
expect('hello').toBe('hello');
});
test('string matches regex', () => {
expect('hello world').toMatch(/world/);
});3. Testing Booleans
Use toBe for strict boolean checks.
test('boolean values', () => {
expect(true).toBe(true);
expect(false).toBe(false);
});4. Tuple Types
Testing tuple types in Jest involves ensuring that arrays with fixed types and lengths (tuples) conform to the expected structure and values. Tuples are common in TypeScript for representing small, structured collections of values.
Testing Tuple Values
For fixed-length arrays with specific types, you can directly assert the structure and values using Jest matchers.
Example:
type Point = [number, number];
test('tuple matches expected structure', () => {
const point: Point = [1, 2];
expect(point).toEqual([1, 2]); // Matches value and order
});Plain Object Types
Testing plain object types in Jest typically involves validating their structure, properties, and values. Jest provides several matchers specifically designed for testing plain JavaScript objects, making it easy to handle objects with nested properties, optional keys, and specific types.
Example:
Use toEqual to check if two objects have the same structure and values.
test('plain object equality', () => {
const obj = { name: 'Alice', age: 30 };
expect(obj).toEqual({ name: 'Alice', age: 30 });
});Why Use Jest Parameterized Tests?
Jest parameterized tests help you avoid repeating the same test logic for multiple inputs.
Instead of writing separate test cases for each scenario, you define the logic once and supply different data sets.
- Reduce duplicate test code: Instead of maintaining multiple tests with identical logic, you can centralize the test and vary only the input data. This makes tests easier to maintain and update.
- Cover more scenarios: A single parameterized test can validate multiple input combinations, helping you increase test coverage without increasing test duplication.
- Make test cases easier to expand: Adding new scenarios becomes as simple as adding another row to the dataset, without modifying the test logic itself.
- Keep test logic separate from test data: Parameterized tests make it easier to see what the test is doing and which inputs it is checking. This becomes particularly useful when a test contains many scenarios or when the test data changes.
- Make failures easier to identify: Jest can include parameter values in generated test names. A descriptive test name makes it easier to identify the exact input that caused a failure.
Common Mistakes to Avoid With Jest Parameterized Tests
Here are the common mistakes you can avoid with Jest tests:
- Combining unrelated scenarios: Avoid grouping different test behaviors into one parameterized table, as it makes tests harder to read and maintain.
- Using unclear test data: Prefer named objects over index-based arrays so QA teams can easily understand what each value represents.
- Creating oversized test tables: Keep datasets focused and small; large tables make failures harder to debug and slow down test comprehension.
- Parameterizing fundamentally different logic: Only parameterize tests that share the same structure; separate tests are better when setup or assertions differ significantly.
Why should you test Jest tests on Real Devices?
While Jest is primarily a testing framework for unit and integration tests, directly testing Jest tests on real devices isn’t typically necessary. Jest’s core functionality focuses on testing JavaScript code in a controlled environment, often using mock functions and virtual DOM implementations.
However, there are specific scenarios where testing on real devices can provide valuable insights:
- Real-World Behavior: Real devices can expose issues related to browser-specific quirks, device performance, and network conditions that might not be apparent in simulated environments.
- User Experience: Testing on real devices can help identify potential UI/UX problems that might not be visible in emulators or simulators.
- Integration with Other Technologies: If your application interacts with other technologies like native APIs, third-party libraries, or hardware sensors, testing on real devices can ensure seamless integration.
- Pixel-Perfect Comparisons: Real devices can help identify visual discrepancies that might be missed in simulated environments, especially when dealing with complex layouts, animations, or device-specific rendering differences.
- Real-World Performance Metrics: Testing on real devices can provide insights into actual performance metrics like load times, frame rates, and resource usage, which can vary significantly across devices.
Setting up Jest for Parameterized Tests
Setting up Jest for parameterized tests involves using the test.each or it.each methods, which allow you to run the same test logic across multiple sets of data inputs. Here’s a step-by-step guide to setting up and using parameterized tests effectively in Jest:
1. Install Jest
If Jest is not already installed in your project, set it up with:
npm install --save-dev jest
Ensure your package.json includes a script to run Jest:
"scripts": {
"test": "jest"
}Run the tests using:
npm test
2. Understanding Parameterized Tests in Jest
Parameterized tests in Jest are achieved using:
- test.each: Runs a test multiple times with different sets of data.
- it.each: Alias for test.each.
These functions allow you to define test cases dynamically, reducing code duplication.
3. Writing Parameterized Tests
The it.each function takes an array of test cases and a callback function. Each test case is passed as arguments to the callback function.
test.each([
[1, 1, 2],
[2, 2, 4],
[5, 2, 7],
])('adds %i + %i to equal %i', (a, b, expected) => {
expect(a + b).toBe(expected);
});Breakdown:
- it.each: This function is used to parameterize the test cases.
- [[1, 1, 2], [2, 2, 4], [5, 2, 7]]: This is an array of test cases, each containing an array of input values and the expected output.
- ‘adds %i + %i to equal %i’: This is the test title, where %i placeholders will be replaced with the actual values from each test case.
- (a, b, expected) => { … }: This is the callback function that will be executed for each test case. The a, b, and expected parameters will be assigned the values from the corresponding test case.
Conclusion
By using Jest parameterized tests, you can club the same test that has different values by parameterizing values and standardizing test cases.
This helps enhance test coverage across multiple web components in your UI, thus reducing rewriting test scripts or running regressions.
Running Jest tests on a cloud platform like BrowserStack ensures scalability and seamless cross-browser testing.









