Тестирование error handling

В процессе тестирования приложений с использованием фреймворка Jasmine необходимо уделить внимание не только корректным, но и некорректным путям выполнения программы, таким как обработка ошибок. Ошибки могут быть связаны с различными факторами, включая неправильный ввод данных, сбои внешних сервисов, ошибки при асинхронных операциях и прочее. Тестирование таких случаев важно для обеспечения надежности и устойчивости к сбоям. Jasmine предоставляет несколько методов для тестирования обработки ошибок, как синхронных, так и асинхронных.

Синхронные ошибки

Для тестирования синхронных ошибок необходимо использовать метод toThrow(). Этот метод позволяет проверить, выбрасывает ли код ожидаемую ошибку в процессе выполнения.

Пример:

describe('Тестирование синхронных ошибок', () => {
  it('должен выбросить ошибку при некорректном значении', () => {
    function throwError() {
      throw new Error('Некорректное значение');
    }

    expect(throwError).toThrowError('Некорректное значение');
  });
});

В этом примере создается функция throwError(), которая выбрасывает ошибку с сообщением «Некорректное значение». Тест проверяет, что при вызове этой функции будет выброшена именно такая ошибка.

Метод toThrow() позволяет также проверять тип ошибки. Например:

describe('Тестирование синхронных ошибок', () => {
  it('должен выбросить ошибку типа TypeError', () => {
    function throwError() {
      throw new TypeError('Тип ошибки неправильный');
    }

    expect(throwError).toThrowError(TypeError);
  });
});

Здесь тест проверяет, что выбрасываемая ошибка является экземпляром TypeError.

Проверка ошибок с помощью регулярных выражений

Jasmine также позволяет проверять ошибки с помощью регулярных выражений, что полезно, когда важно только сообщение об ошибке, но не его точное содержание.

describe('Тестирование синхронных ошибок', () => {
  it('должен выбросить ошибку с сообщением, содержащим некорректный ввод', () => {
    function throwError() {
      throw new Error('Некорректный ввод данных');
    }

    expect(throwError).toThrowError(/Некорректный ввод/);
  });
});

В этом примере тест проверяет, что сообщение об ошибке содержит строку «Некорректный ввод», но не обязательно совпадает полностью.

Асинхронные ошибки

При тестировании асинхронных операций, таких как запросы к серверу или таймеры, ошибки могут возникать позже, чем код выполнения теста. Для обработки таких ошибок Jasmine предоставляет несколько инструментов, включая done() для выполнения асинхронных тестов и async/await для работы с промисами.

Использование done() для асинхронных ошибок

В случае использования асинхронных операций в тестах, когда необходимо дождаться завершения операции перед утверждениями, можно использовать функцию done(). Эта функция уведомляет Jasmine о завершении асинхронного теста.

describe('Тестирование асинхронных ошибок', () => {
  it('должен выбросить ошибку при отказе в запросе', (done) => {
    function makeRequest(callback) {
      setTimeout(() => {
        callback(new Error('Ошибка при запросе'));
      }, 100);
    }

    makeRequest((error) => {
      expect(error).toBeInstanceOf(Error);
      expect(error.message).toBe('Ошибка при запросе');
      done();
    });
  });
});

В этом примере создается асинхронная функция makeRequest, которая симулирует ошибку через таймер. После завершения асинхронной операции вызывается метод done(), чтобы уведомить Jasmine о завершении теста.

Асинхронное тестирование с использованием async/await

Современные подходы к асинхронному тестированию часто включают использование async и await для работы с промисами, что делает код более читаемым и удобным для понимания.

describe('Тестирование асинхронных ошибок с async/await', () => {
  it('должен выбросить ошибку при отказе в запросе', async () => {
    function makeRequest() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          reject(new Error('Ошибка при запросе'));
        }, 100);
      });
    }

    try {
      await makeRequest();
    } catch (error) {
      expect(error).toBeInstanceOf(Error);
      expect(error.message).toBe('Ошибка при запросе');
    }
  });
});

Здесь используется промис для асинхронной операции, и через await дожидаемся его завершения. Если ошибка возникает, она перехватывается в блоке catch, и далее выполняются утверждения.

Тестирование ошибок в промисах

Промисы — это частый источник ошибок в асинхронном коде. Чтобы проверить, правильно ли обрабатываются ошибки в промисах, можно использовать методы catch и reject.

describe('Тестирование ошибок в промисах', () => {
  it('должен выбросить ошибку при отклонении промиса', async () => {
    function makeRequest() {
      return Promise.reject(new Error('Ошибка при запросе'));
    }

    await expectAsync(makeRequest()).toBeRejectedWithError('Ошибка при запросе');
  });
});

В этом примере тестируется промис, который отклоняется с ошибкой. Метод expectAsync() позволяет тестировать промисы и проверять, что они отклоняются с ожидаемой ошибкой.

Проверка ошибок с использованием фреймворков

Иногда тестирование обработки ошибок может включать интеграцию с другими библиотеками, такими как fetch для HTTP-запросов или сторонними API. В таких случаях важно правильно мокировать зависимости, чтобы тесты не зависели от реальных внешних факторов.

Пример с мокированием HTTP-запросов

Для тестирования кода, который зависит от внешнего API, можно использовать моки и шпионы. Например, с использованием spyOn можно подменить функцию fetch и эмулировать ошибку при запросе.

describe('Тестирование HTTP-запросов с мокированием', () => {
  it('должен выбросить ошибку при неудачном запросе', async () => {
    spyOn(window, 'fetch').and.returnValue(Promise.reject(new Error('Ошибка запроса')));

    try {
      await fetch('https://example.com/data');
    } catch (error) {
      expect(error).toBeInstanceOf(Error);
      expect(error.message).toBe('Ошибка запроса');
    }
  });
});

Здесь с помощью spyOn мы подменяем реализацию fetch, чтобы она всегда отклонялась с ошибкой. Это позволяет тестировать логику обработки ошибок без необходимости совершать реальные сетевые запросы.

Выводы

Тестирование обработки ошибок — важная часть тестирования устойчивости и надежности приложения. Jasmine предоставляет богатый набор инструментов для тестирования ошибок как в синхронном, так и в асинхронном коде. Правильная обработка ошибок в тестах позволяет уверенно выявлять и устранять потенциальные сбои в программном обеспечении.