Как проверить тип сгенерированного исключения в Jest


161

Я работаю с некоторым кодом, где мне нужно проверить тип исключения, созданного функцией (это TypeError, ReferenceError и т. Д.).

Моя текущая среда тестирования - это AVA, и я могу протестировать ее как t.throwsметод второго аргумента , например, здесь:

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', (t) => {
  const error = t.throws(() => {
    throwError();
  }, TypeError);

  t.is(error.message, 'UNKNOWN ERROR');
});

Я начал переписывать свои тесты на Jest и не мог найти, как это легко сделать. Это вообще возможно?

Ответы:


225

В Jest вы должны передать функцию в expect (function) .toThrow (пусто или тип ошибки).

Пример:

test("Test description", () => {
  const t = () => {
    throw new TypeError();
  };
  expect(t).toThrow(TypeError);
});

Если вам нужно проверить существующую функцию, генерирует ли она набор аргументов, вы должны заключить ее в анонимную функцию в expect ().

Пример:

test("Test description", () => {
  expect(() => {http.get(yourUrl, yourCallbackFn)}).toThrow(TypeError);
});

79

Немного странно, но работает, и imho хорошо читается:

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', () => {
  try {
      throwError();
      // Fail test if above expression doesn't throw anything.
      expect(true).toBe(false);
  } catch (e) {
      expect(e.message).toBe("UNKNOWN ERROR");
  }
});

Catchblock поймать ваше исключение, затем вы можете протестировать свой поднятый Error. Странно expect(true).toBe(false);нужно провалить тест, если ожидаемый Errorне будет брошен. В противном случае эта линия никогда не будет доступна ( Errorдолжна быть поднята перед ними).

EDIT: @Kenny Body предлагает лучшее решение, которое улучшает качество кода, если вы используете expect.assertions()

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', () => {
  expect.assertions(1);
  try {
      throwError();
  } catch (e) {
      expect(e.message).toBe("UNKNOWN ERROR");
  }
});

См. Исходный ответ с дополнительными пояснениями: Как проверить тип брошенного исключения в Jest


18
Это очень подробный способ проверки исключений, когда в Jest уже есть способ проверки на исключения expect.toThrow (): jestjs.io/docs/en/expect.html#tothrowerror
gomisha

4
Да, но он проверяет только тип, а не сообщение или другой контент, и вопрос был о тестовом сообщении, а не о типе.
bodolsog 09

2
Хах. Очень нравится этот, поскольку мой код должен проверить значение выданной ошибки, поэтому мне нужен экземпляр. Я бы написал ошибочное ожидание expect('here').not.toBe('here');просто для удовольствия :-)
Валерий

4
@Valery или: expect('to be').not.toBe('to be')в стиле Шекспира.
Мишель ван дер Блонк

2
самый недооцененный ответ!
Эдвин Икечукву Оконкво

42

Я использую более лаконичный вариант:

expect(() => {
  //code block that should throw error
}).toThrow(TypeError) //or .toThrow('expectedErrorMessage')

2
Коротко и точно.
flapjack

33

Из моего (хотя и ограниченного) знакомства с Jest я обнаружил, что expect().toThrow()это подходит, если вы хотите ТОЛЬКО проверить, выдается ли ошибка определенного типа:

expect(() => functionUnderTest()).toThrow(TypeError);

ИЛИ выдается ошибка с конкретным сообщением:

expect(() => functionUnderTest()).toThrow('Something bad happened!');

Если вы попытаетесь сделать оба, вы получите ложное срабатывание. Например, если ваш код выдает ошибку RangeError('Something bad happened!'), этот тест пройдет:

expect(() => functionUnderTest()).toThrow(new TypeError('Something bad happened!'));

Ответ bodolsog, предлагающий использовать try / catch, близок, но вместо того, чтобы ожидать, что истина будет ложной, чтобы гарантировать, что ожидаемые утверждения в catch будут выполнены, вы можете вместо этого использовать expect.assertions(2)в начале вашего теста, где 2- количество ожидаемых утверждений , Мне кажется, это более точно описывает цель теста.

Полный пример проверки типа И сообщения об ошибке:

describe('functionUnderTest', () => {
    it('should throw a specific type of error.', () => {
        expect.assertions(2);

        try {
            functionUnderTest();
        } catch (error) {
            expect(error).toBeInstanceOf(TypeError);
            expect(error).toHaveProperty('message', 'Something bad happened!');
        }
    }); 
});

Если functionUnderTest()НЕ выдает ошибку, утверждения будут выполнены, но expect.assertions(2)не будут выполнены и тест не пройден.


D'о. Я всегда забываю об ожидаемой функции множественных утверждений в Jest (возможно, я просто не считаю ее наиболее интуитивно понятной, но она определенно работает в таких случаях!) Ура!
kpollock

Это отлично сработало для меня. Это следует использовать.
Анкит Танна,

expect.hasAssertions()может быть лучшей альтернативой, когда тест не имеет никаких утверждений снаружи catch, потому что вам не нужно обновлять число, если вы добавляете / удаляете утверждения.
Андре Сасси

12

Сам не пробовал, но я бы предложил использовать утверждение Jest toThrow . Думаю, ваш пример будет выглядеть примерно так:

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', (t) => {
  const error = t.throws(() => {
    throwError();
  }, TypeError);

  expect(t).toThrowError('UNKNOWN ERROR');
  //or
  expect(t).toThrowError(TypeError);
});

Опять же, не тестировал, но я думаю, что он должен работать.


8

У шутки есть метод toThrow(error) для проверки того, что функция выдает при ее вызове.

Итак, в вашем случае вы должны назвать это так:

expect(t).toThrowError(TypeError);

Документы


1
Это не будет работать в случае: jest.spyOn(service, 'create').mockImplementation(() => { throw new Error(); });если Передразнивало метод createне является async.
Сергей

7

Современные шутки позволяют проводить больше проверок отклоненных значений. Например:

const request = Promise.reject({statusCode: 404})
await expect(request).rejects.toMatchObject({ statusCode: 500 });

выйдет из строя с ошибкой

Error: expect(received).rejects.toMatchObject(expected)

- Expected
+ Received

  Object {
-   "statusCode": 500,
+   "statusCode": 404,
  }

6

В документации ясно, как это сделать. Допустим, у меня есть функция, которая принимает два параметра, и она выдаст ошибку, если один из них null.

function concatStr(str1, str2) {
  const isStr1 = str1 === null
  const isStr2 = str2 === null
  if(isStr1 || isStr2) {
    throw "Parameters can't be null"
  }
  ... // continue your code

Ваш тест

describe("errors", () => {
  it("should error if any is null", () => {
    // notice that the expect has a function that returns the function under test
    expect(() => concatStr(null, "test")).toThrow()
  })
})


3

В итоге я написал удобный метод для нашей библиотеки test-utils

/**
 *  Utility method to test for a specific error class and message in Jest
 * @param {fn, expectedErrorClass, expectedErrorMessage }
 * @example   failTest({
      fn: () => {
        return new MyObject({
          param: 'stuff'
        })
      },
      expectedErrorClass: MyError,
      expectedErrorMessage: 'stuff not yet implemented'
    })
 */
  failTest: ({ fn, expectedErrorClass, expectedErrorMessage }) => {
    try {
      fn()
      expect(true).toBeFalsy()
    } catch (err) {
      let isExpectedErr = err instanceof expectedErrorClass
      expect(isExpectedErr).toBeTruthy()
      expect(err.message).toBe(expectedErrorMessage)
    }
  }

То же самое можно сделать, используя собственные функции Jests. См. Мой ответ о том, как это можно сделать - stackoverflow.com/a/58103698/3361387
Kenny Body

3

В дополнение к сообщению Питера Дэниса просто хотел подчеркнуть часть его решения, включающую «[передачу] функции в expect (function) .toThrow (blank или type of error)».

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

Неправильный (но логичный подход большинства людей):

expect(functionUnderTesting();).toThrow(ErrorTypeOrErrorMessage);

Правильно:

expect(() => { functionUnderTesting(); }).toThrow(ErrorTypeOrErrorMessage);

Это очень странно, но тестирование должно пройти успешно.


1

пытаться
expect(t).rejects.toThrow()


4
Почему try? там не пробовать - но ответь. Если это ответ, то просьба уточнить подробности. что вы добавляете к существующему ответу?
dWinder

7
Я думаю, что @Razim говорил, что вы должны попробовать решение, а не использовать try catch.
Tom
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.