Custom Jest Reporter for Enhanced Test Reporting
Jest is a powerful testing framework, but sometimes the default reporter doesn't provide the level of detail or customization needed for complex projects. This challenge involves creating a custom Jest reporter that logs test results in a structured JSON format, making it easier to integrate with CI/CD pipelines, build custom dashboards, or perform post-test analysis.
Problem Description
Your task is to create a custom Jest reporter that intercepts and logs test results. This reporter should output a JSON object containing information about each test file, including the status of each test case within that file, and an overall summary of the test run.
Key Requirements:
- JSON Output: The reporter must write its output to a specified file in JSON format.
- Detailed Test Information: For each test file, the reporter should capture:
- The file path.
- The status of each individual test case (e.g., "passed", "failed", "skipped").
- For failed tests, include the error message and stack trace.
- Summary Statistics: The overall report should include summary statistics such as:
- Total number of tests.
- Number of passed tests.
- Number of failed tests.
- Number of skipped tests.
- Total duration of the test run.
- Configurability: The reporter should be configurable to specify the output file path.
Expected Behavior:
When Jest runs tests with your custom reporter enabled, a JSON file will be generated at the specified path containing the structured test results.
Edge Cases:
- Tests that are skipped.
- Tests that have multiple failures (though typically Jest stops on the first failure per test).
- The reporter needs to handle the lifecycle events of a Jest test run gracefully.
Examples
Example 1: A Simple Test Suite
Assume you have a file math.test.ts with the following content:
// math.test.ts
describe('Math functions', () => {
test('should add two numbers correctly', () => {
expect(2 + 2).toBe(4);
});
test('should subtract two numbers correctly', () => {
expect(5 - 3).toBe(2);
});
});
And your Jest configuration points to your custom reporter and an output file named test-results.json.
Input: Jest runs math.test.ts.
Output (test-results.json):
{
"testResults": [
{
"filePath": "math.test.ts",
"tests": [
{
"fullName": "Math functions should add two numbers correctly",
"status": "passed",
"duration": 5, // Example duration in ms
"error": null
},
{
"fullName": "Math functions should subtract two numbers correctly",
"status": "passed",
"duration": 3, // Example duration in ms
"error": null
}
]
}
],
"summary": {
"totalTests": 2,
"passedTests": 2,
"failedTests": 0,
"skippedTests": 0,
"startTime": "2023-10-27T10:00:00.000Z", // Example start time
"endTime": "2023-10-27T10:00:05.000Z", // Example end time
"duration": 5000 // Example total duration in ms
}
}
Explanation: The JSON reflects two passing tests within the math.test.ts file. The summary shows a total of two tests, all of which passed.
Example 2: A Test Suite with a Failure and a Skip
Assume you have a file string.test.ts with the following content:
// string.test.ts
describe('String functions', () => {
test('should return the length of a string', () => {
expect('hello'.length).toBe(5);
});
test('should convert a string to uppercase', () => {
expect('world'.toUpperCase()).toBe('WORLD');
});
test.skip('should reverse a string', () => {
expect('abc'.split('').reverse().join('')).toBe('cba');
});
test('should check if a string is a palindrome', () => {
expect('madam').toBe('madam'); // Intentional failure
});
});
Input: Jest runs string.test.ts.
Output (test-results.json - appended or new file):
{
"testResults": [
{
"filePath": "string.test.ts",
"tests": [
{
"fullName": "String functions should return the length of a string",
"status": "passed",
"duration": 4,
"error": null
},
{
"fullName": "String functions should convert a string to uppercase",
"status": "passed",
"duration": 2,
"error": null
},
{
"fullName": "String functions should reverse a string",
"status": "skipped",
"duration": 0,
"error": null
},
{
"fullName": "String functions should check if a string is a palindrome",
"status": "failed",
"duration": 6,
"error": {
"message": "Expect \"madam\" to be \"madam\".", // Note: Jest might show a more specific error
"stack": "Error: ...\n at ...\n at ..." // Actual stack trace
}
}
]
}
],
"summary": {
"totalTests": 4,
"passedTests": 2,
"failedTests": 1,
"skippedTests": 1,
"startTime": "2023-10-27T10:01:00.000Z",
"endTime": "2023-10-27T10:01:10.000Z",
"duration": 10000
}
}
Explanation: This output shows one skipped test and one failed test, along with their respective statuses. The summary reflects these counts accurately. The failed test includes an error object with message and stack.
Constraints
- The reporter should be written in TypeScript.
- The output JSON file must be valid JSON.
- The reporter must correctly handle Jest's test lifecycle events (e.g.,
testResults,done). - The output file path should be configurable, defaulting to
jest-report.jsonif not specified. - The reporter should not introduce significant overhead to the test execution time.
Notes
- You will need to create a TypeScript class that implements Jest's
Reporterinterface (or a compatible structure). - Refer to the Jest documentation on custom reporters for guidance on the interface and event handling.
- Consider how you will serialize the output to JSON, especially for error objects.
- You'll need to configure your Jest environment to use your custom reporter. This is typically done in your
jest.config.jsorjest.config.tsfile. - Think about how you will aggregate results from multiple test files into a single, coherent JSON report.