Як перевірити тип викинутого винятку в 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 вам потрібно передати функцію очікувати (функцію) .toThrow (порожня або тип помилки).

Приклад:

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

Якщо вам потрібно перевірити існуючу функцію, чи вона кидає з набором аргументів, вам доведеться загорнути її всередині анонімної функції в очікуванні ().

Приклад:

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

79

Трохи дивно, але працює і імхо добре читається:

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");
  }
});

Catchблок зловити ваш виняток, тоді ви можете протестувати на піднятому 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 вже є спосіб очікування
— gomisha

4
Так, але він перевіряє лише тип, а не повідомлення чи інший вміст. Питання стосувалося тестового повідомлення, а не типу.
— bodolsog

2
Ха-ха. Дійсно, як цей, як мій код повинен перевірити значення викинутої помилки, тому мені потрібен екземпляр. Я б написав несправедливе очікування, як expect('here').not.toBe('here');просто заради задоволення від цього :-)
— Валерій

4
@ Валері або: expect('to be').not.toBe('to be')у стилі Шекспіра
— Міхель ван дер Блок

2
найбільш занижена відповідь!
— Edwin Ikechukwu Okonkwo

41

Я використовую трохи більш стисну версію:

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, яка передбачає використання спроби / лову, є близькою, але замість того, щоб очікувати, що правда буде хибною, щоб гарантувати, що твердження про очікування в улові потрапили, ви можете замість цього використати 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'oh. Я завжди забуваю про те, що я очікую на кілька тверджень Jest (можливо, я просто не вважаю це найбільш інтуїтивним, але це безумовно працює для таких випадків!) Привіт!
— kpollock

Це для мене спрацювало чудово. Це слід використовувати.
— Анкіт Танна

expect.hasAssertions()може бути кращою альтернативою, коли тест не має жодних тверджень зовні catch, тому що вам не доведеться оновлювати номер, якщо ви додаєте / видаляєте твердження.
— Андре Сассі

12

Я сам не пробував цього, але я б запропонував скористатися твердженням Джеста для того, щоб скасувати . Тож я думаю, що ваш приклад виглядав би приблизно так:

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

Jest має метод 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

Я написав зручний метод для нашої бібліотеки тестових утиліт

/**
 *  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

Далі до поста Пітера Даніса просто хотілося підкреслити частину його рішення, що передбачає "[передачу] функції в очікування (функцію) .toThrow (порожня або помилка типу)".

У Jest, коли ви перевіряєте на випадок, коли має бути видалена помилка, в рамках очікування () обгортання функції, яку випробовуєте, потрібно надати ще один додатковий шар стрічки для обгортання, щоб він працював. Тобто

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

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

Право:

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

Це дуже дивно, але слід змусити тестування пройти успішно.


1

спробуйте
expect(t).rejects.toThrow()


4
Чому try? немає спроб - але відповідь. Якщо це відповідь, уточнюйте детальніше. що ви додаєте до існуючої відповіді?
— dWinder

7
Я думаю, що @Razim казав, що ви повинні спробувати рішення, а не використовувати спробу.
— Том
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.