Hone logo
Hone
Problems

Jest Mutation Testing: Eliminate Mutant Threats!

Mutation testing is a powerful technique to assess the quality of your unit tests. It works by introducing small, artificial errors ("mutations") into your code and verifying that your tests catch these changes. This challenge focuses on implementing a Jest-based solution to "kill" these mutants, demonstrating the effectiveness of your test suite.

Problem Description

You are tasked with creating a Jest test suite that effectively detects and "kills" mutants introduced into a simple TypeScript function. The goal is to write tests that fail when a mutation is present, proving that the tests are sensitive enough to catch subtle errors. You'll be provided with a base function and a set of predefined mutants. Your job is to write Jest tests that will fail when any of these mutants are active. The "killing" of a mutant means the test suite fails when that mutant is injected.

What needs to be achieved:

  1. Write a Jest test suite for the provided add function.
  2. The test suite must fail when any of the provided mutants are injected into the add function.
  3. The tests should be robust and cover various scenarios to ensure comprehensive mutant detection.

Key Requirements:

  • Use Jest as the testing framework.
  • The solution must be written in TypeScript.
  • The tests should be clear, concise, and well-documented.
  • The tests should cover edge cases (e.g., negative numbers, zero).

Expected Behavior:

When a mutant is injected into the add function, the Jest test suite should fail, indicating that the mutant was not caught by the existing tests. When the original, unmutated add function is used, the tests should pass.

Edge Cases to Consider:

  • Negative numbers as input.
  • Zero as input.
  • Large numbers (potential overflow issues, though not a primary focus).
  • Different combinations of inputs to ensure all code paths are tested.

Examples

Example 1:

Input: add = (a: number, b: number): number => a + b;
Mutant: add = (a: number, b: number): number => a - b;
Tests:
  it('should add two positive numbers', () => {
    expect(add(2, 3)).toBe(5);
  });
  it('should handle negative numbers', () => {
    expect(add(-1, 5)).toBe(4);
  });
Output: Test failure. The test suite fails because the mutant subtracts instead of adds.
Explanation: The mutant introduces a subtraction operation, causing the tests to fail.

Example 2:

Input: add = (a: number, b: number): number => a + b;
Mutant: add = (a: number, b: number): number => a;
Tests:
  it('should add two positive numbers', () => {
    expect(add(2, 3)).toBe(5);
  });
  it('should handle zero', () => {
    expect(add(0, 5)).toBe(5);
  });
Output: Test failure. The test suite fails because the mutant always returns the first argument.
Explanation: The mutant always returns the first argument, causing the tests to fail.

Example 3: (Edge Case)

Input: add = (a: number, b: number): number => a + b;
Mutant: add = (a: number, b: number): number => a * b;
Tests:
  it('should add two positive numbers', () => {
    expect(add(2, 3)).toBe(5);
  });
  it('should handle negative numbers', () => {
    expect(add(-1, 5)).toBe(4);
  });
  it('should handle zero', () => {
    expect(add(0, 5)).toBe(5);
  });
Output: Test failure. The test suite fails because the mutant multiplies instead of adds.
Explanation: The mutant introduces multiplication, causing the tests to fail across multiple test cases.

Constraints

  • The add function is provided and cannot be modified directly. You will be injecting mutants into it for testing purposes.
  • The test suite must be written in TypeScript and compatible with Jest.
  • The solution should be reasonably efficient. While performance is not the primary concern, avoid excessively complex or inefficient test logic.
  • You are provided with a set of mutants. Your tests must be able to detect all of them.

Notes

  • This challenge simulates a mutation testing environment. You won't be using a full mutation testing library, but rather manually injecting mutants for testing.
  • Consider using a variety of test cases to cover different code paths and input values.
  • Think about how to structure your tests to maximize coverage and ensure that each mutant is effectively challenged.
  • The provided add function is intentionally simple to focus on the testing aspect of mutation testing.
  • The goal is to write tests that fail when the mutants are present. Passing tests with the original function are a prerequisite.
const add = (a: number, b: number): number => a + b;
Loading editor...
typescript