Logo
Published on

Simplifying tests with Jest test.each

Authors
  • avatar
    Name
    Emanoel Oliveira
    Twitter

The problem

When testing a function, it's very common to have a mental map of all the input and output scenarios and then write the unit tests to cover all the code.

Here's an example of a hypothetical function whose purpose is to receive the name of a color and return its hexadecimal:

// Auxiliary enum with hex map
enum HexColors {
  black = '#000000',
  white = '#ffffff',
  red = '#dc2626',
  blue = '#3b82f6',
  yellow = '#fde047',
}

// Function that converts a color by name to hexadecimal
function colorToHex(colorName: string): string {
  const colorMap = {
    black: HexColors.black,
    white: HexColors.white,
    red: HexColors.red,
    blue: HexColors.blue,
    yellow: HexColors.yellow,
  };

  const hexColor = colorMap[colorName];

  return hexColor ?? HexColors.white;
}

The most common solution

Usually in such a scenario, the most common code looks something like this:

describe('hard way', () => {
  it('should return #000000, when 0 is given', () => {
    const result = colorToHex('black');
    expect(result).toBe(HexColors.black);
  });

  it('should return #ffffff, when 1 is given', () => {
    const result = colorToHex('white');
    expect(result).toBe(HexColors.white);
  });

  it('should return #dc2626, when 2 is given', () => {
    const result = colorToHex('red');
    expect(result).toBe(HexColors.red);
  });

  it('should return #3b82f6, when 3 is given', () => {
    const result = colorToHex('blue');
    expect(result).toBe(HexColors.blue);
  });

  it('should return #fde047, when 4 is given', () => {
    const result = colorToHex('yellow');
    expect(result).toBe(HexColors.yellow);
  });

  it('should return #ffffff, when 5 is given', () => {
    const result = colorToHex('magenta');
    expect(result).toBe(HexColors.white);
  });
});

These tests run and pass, but it is a very repetitive job since the same test is repeated 6 times, only changing the input and output parameters.

The optimized solution

You can get the same result with less effort and in a more optimized way, without having to write the same test 6 times and manually changing the input and output variables, with test.each in jest.

const testCases = [
  { input: 'black', output: '#000000' },
  { input: 'white', output: '#ffffff' },
  { input: 'red', output: '#dc2626' },
  { input: 'blue', output: '#3b82f6' },
  { input: 'yellow', output: '#fde047' },
  { input: 'magenta', output: '#ffffff' },
];

describe('easy way', () => {
  test.each(testCases)(
    'should return $output, when $input is given',
    ({ input, output }) => {
      const result = colorToHex(input);
      expect(result).toBe(output);
    },
  );
});

With this new approach we have several benefits, starting with the fact that the code is more succinct and still readable and easy to maintain, and also writing the test only once, since this code will run through all the test scenarios defined in testCases.

Conclusão

Test code doesn't have to take the same care as production code, but there are ways to make it more readable, performant and, above all, less laborious. So every time you realize that you're repeating the same test several times, remember test.each.