Labs ICT
Pro Login

Testing Exceptions

Making sure errors are thrown when they should be.

Testing Exceptions

What happens when your function receives bad input? Does it handle it gracefully, or does it crash your entire app? Testing exceptions is how you make sure your code fails the right way.

One thing that confused me at first was why I needed to test errors. Turns out, testing that your code fails correctly is just as important as testing that it works.

Using toThrow

Jest gives you toThrow() to check if a function throws an error. You wrap the function call in a function and Jest handles the rest.


function divide(a, b) {
  if (b === 0) {
    throw new Error('Cannot divide by zero');
  }
  return a / b;
}

describe('divide', () => {
  it('throws error when dividing by zero', () => {
    expect(() => divide(10, 0)).toThrow('Cannot divide by zero');
  });

  it('returns result for valid inputs', () => {
    expect(divide(10, 2)).toBe(5);
  });
});
    

Notice the arrow function wrapper around divide(10, 0). That is important. You need to pass a function to toThrow, not the result of calling the function.

Testing Specific Error Types

Sometimes you want to check the type of error, not just the message. This is useful when you throw custom error classes.


class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.field = field;
    this.name = 'ValidationError';
  }
}

function validateAge(age) {
  if (age < 0 || age > 150) {
    throw new ValidationError('age', 'Age must be between 0 and 150');
  }
  return true;
}

it('throws ValidationError for invalid age', () => {
  expect(() => validateAge(-5)).toThrow(ValidationError);
  expect(() => validateAge(-5)).toThrow('Age must be between 0 and 150');
});
    

Testing Rejected Promises

If your function returns a promise that rejects, you use rejects instead.


async function fetchData(url) {
  if (!url) {
    throw new Error('URL is required');
  }
  return { data: 'some data' };
}

it('rejects when url is empty', async () => {
  await expect(fetchData('')).rejects.toThrow('URL is required');
});
    
Try it Yourself →

Key Takeaways

  • Use toThrow() to check if a function throws an error
  • Wrap the function call in an arrow function when using toThrow
  • You can check both error messages and error types
  • Use rejects.toThrow() for async functions that should fail
  • Testing error paths is just as important as testing happy paths