Как написать тест, который ожидает, что ошибка будет выдана в Жасмин?


490

Я пытаюсь написать тест для Jasmine Test Framework, который ожидает ошибку. В данный момент я использую интеграцию Jasmine Node.js из GitHub .

В моем модуле Node у меня есть следующий код:

throw new Error("Parsing is not possible");

Теперь я пытаюсь написать тест, который ожидает эту ошибку:

describe('my suite...', function() {
    [..]
    it('should not parse foo', function() {
    [..]
        expect(parser.parse(raw)).toThrow(new Error("Parsing is not possible"));
    });
});

Я попробовал также Error()и некоторые другие варианты и просто не могу понять, как заставить это работать.


4
Чтобы передать аргументы тестируемой функции без использования анонимной функции, попробуйте Function.bind: stackoverflow.com/a/13233194/294855
— Даниал Айтекин

Ответы:


802

Вы должны передавать функцию в expect(...)вызов. Код у вас здесь:

// incorrect:
expect(parser.parse(raw)).toThrow(new Error("Parsing is not possible"));

пытается на самом деле вызвать parser.parse(raw) в попытке передать результат в expect(...),

Попробуйте вместо этого использовать анонимную функцию:

expect( function(){ parser.parse(raw); } ).toThrow(new Error("Parsing is not possible"));

28
Если вам также не нужно передавать аргументы, вы также можете просто передать функцию, чтобы ожидать:expect(parser.parse).toThrow(...)
— SubmittedDenied

60
Полезный совет: вы можете просто позвонить expect(blah).toThrow(). Отсутствие аргументов означает проверку, чтобы увидеть, что он бросает вообще. Соответствие строк не требуется. См. Также: stackoverflow.com/a/9525172/1804678
— Джесс

1
На мой взгляд, это более очевидно в отношении цели теста при добавлении в анонимную функцию. Кроме того, он остается неизменным среди всех тестов, когда, например, вам необходимо передать параметры целевой функции, чтобы она сработала.
— Beez

10
@SubmittedDenied: это вообще не работает! Если parser.parseиспользуется this, передача его без контекста приведет к неожиданным результатам. Вы могли бы пройти parser.parse.bind(parser), но, честно говоря ... анонимная функция была бы более элегантной.
— Мельвенс

2
@LanceKind извините за некро, но причина, по которой вы должны передать функцию, состоит в том, что значение будет оценено и сгенерирует исключение еще до того, как оно будет передано в ожидаемое значение.
— 1gLassitude

68

Ты используешь:

expect(fn).toThrow(e)

Но если вы посмотрите на комментарий функции (ожидается строка):

294 /**
295  * Matcher that checks that the expected exception was thrown by the actual.
296  *
297  * @param {String} expected
298  */
299 jasmine.Matchers.prototype.toThrow = function(expected) {

Я полагаю, вы должны написать это так (используя лямбда-анонимную функцию):

expect(function() { parser.parse(raw); } ).toThrow("Parsing is not possible");

Это подтверждается в следующем примере:

expect(function () {throw new Error("Parsing is not possible")}).toThrow("Parsing is not possible");

Дуглас Крокфорд настоятельно рекомендует этот подход вместо использования «throw new Error ()» (способ создания прототипа):

throw {
   name: "Error",
   message: "Parsing is not possible"
}

3
На самом деле просмотр кода toThrow с радостью примет либо объект исключения, либо / строку. Проверьте звонки, которые он делает к ожидаемому сообщению, например.
— Пит Ходжсон

1
Это похоже на строку как побочный эффект строки, не имеющей свойства сообщения
— mpapis

1
Большое спасибо, что работал. Я все еще принял ответ Пита, потому что его ответ сделал мне более ясным, что я должен использовать лямбду. Еще +1 :-) Спасибо!
— echox

16
Если вы выбрасываете объект, а не ошибку (как в вашем примере внизу), то вы не получите трассировку стека в браузерах, которые его поддерживают.
— kybernetikos

2
@kybernetikos на удивление, не совсем верно; вы все равно получите трассировку стека, напечатанную в консоли Chrome, если выбросите не- Error( jsfiddle.net/k1mxey8j ). Однако у вашего брошенного объекта, разумеется, не будет .stackсвойства, которое может быть важно, если вы хотите настроить автоматическое создание отчетов об ошибках.
— Марк Эмери

24

Более элегантное решение, чем создание анонимной функции, единственной целью которой является обтекание другой, является использование bindфункции es5 . Функция связывания создает новую функцию, для которой при вызове ее thisключевое слово устанавливается на предоставленное значение с заданной последовательностью аргументов, предшествующей любому предоставленному при вызове новой функции.

Вместо:

expect(function () { parser.parse(raw, config); } ).toThrow("Parsing is not possible");

Рассматривать:

expect(parser.parse.bind(parser, raw, config)).toThrow("Parsing is not possible");

Синтаксис bind позволяет вам тестировать функции с разными thisзначениями, и, на мой взгляд, делает тест более читабельным. Смотрите также: https://stackoverflow.com/a/13233194/1248889


23

Я заменяю сопоставление toThrow от Jasmine следующим: оно позволяет сопоставить свойство name исключения или его свойство message. Для меня это облегчает написание тестов и делает их менее хрупкими, поскольку я могу делать следующее:

throw {
   name: "NoActionProvided",
   message: "Please specify an 'action' property when configuring the action map."
}

и затем проверьте следующее:

expect (function () {
   .. do something
}).toThrow ("NoActionProvided");

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

Это замена toThrow, которая позволяет это:

jasmine.Matchers.prototype.toThrow = function(expected) {
  var result = false;
  var exception;
  if (typeof this.actual != 'function') {
    throw new Error('Actual is not a function');
  }
  try {
    this.actual();
  } catch (e) {
    exception = e;
  }
  if (exception) {
      result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected) || this.env.equals_(exception.name, expected));
  }

  var not = this.isNot ? "not " : "";

  this.message = function() {
    if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
      return ["Expected function " + not + "to throw", expected ? expected.name || expected.message || expected : " an exception", ", but it threw", exception.name || exception.message || exception].join(' ');
    } else {
      return "Expected function to throw an exception.";
    }
  };

  return result;
};

4
Хороший подход, но является ли {name: '...', message: '...'} надлежащим объектом Error в JavaScript?
— Марк

1
Хороший комментарий @Marc. Вы правы, свойство name не является стандартным. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… но так ли это неправильно?
— Джесс

4
@Джейк! Я нашел лучший способ !!!! Вы можете просто позвонить expect(blah).toThrow(). Отсутствие аргументов означает проверку, чтобы увидеть, что он бросает на всех. Соответствие строк не требуется. См. Также: stackoverflow.com/a/9525172/1804678
— Джесс

5
Спасибо, Джесс, это правда, но тогда это может привести к какой-то другой ошибке, такой как TypeError, и мой тест пройдет неправильно, маскируя настоящую ошибку.
— Джейк

4
Теперь вы также можете использовать RegEx в качестве аргумента для toThrow ().
— Тони О'Хаган

21

Как упоминалось ранее, необходимо передать функцию, так toThrowкак это функция, которую вы описываете в своем тесте: «Я ожидаю, что эта функция выдает x»

expect(() => parser.parse(raw))
  .toThrow(new Error('Parsing is not possible'));

При использовании Jasmine-Matchers, вы также можете использовать один из следующих вариантов, когда они соответствуют ситуации;

// I just want to know that an error was
// thrown and nothing more about it
expect(() => parser.parse(raw))
  .toThrowAnyError();

или

// I just want to know that an error of 
// a given type was thrown and nothing more
expect(() => parser.parse(raw))
  .toThrowErrorOfType(TypeError);

3
Это expect(foo).toThrowError(TypeError);в Жасмин 2.5: jasmine.github.io/2.5/introduction
— Бенни Нойгебауэр,

9

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

try
   do something
   @fail Error("should send a Exception")
 catch e
   expect(e.name).toBe "BLA_ERROR"
   expect(e.message).toBe 'Message'

Мне, как правило, нравится аспект «самодокументирования» ... это делает очень очевидным, что вы тестируете состояние ошибки
— модулем


3

Для тех, кто все еще может столкнуться с этой проблемой, для меня опубликованное решение не сработало, и оно продолжало выдавать эту ошибку: Error: Expected function to throw an exception. позже я понял, что функция, которую я ожидал выдать ошибку, была асинхронной функцией и ожидала обещания быть отклоненным и затем выдать ошибку, и это то, что я делал в своем коде:

throw new Error('REQUEST ID NOT FOUND');

и вот что я сделал в моем тесте, и это сработало:

it('Test should throw error if request not found', willResolve(() => {
         const promise = service.getRequestStatus('request-id');
                return expectToReject(promise).then((err) => {
                    expect(err.message).toEqual('REQUEST NOT FOUND');
                });
            }));

Спасибо за это. Я был очень смущен, но ваш комментарий имеет смысл. Я исправил проблему, используя новый expectAsync jasmine.github.io/api/3.3/async-matchers.html
— Бенджамин
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.