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

Я пытаюсь написать тест для 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() и некоторые другие варианты и просто не могу понять, как заставить его работать.


person echox    schedule 10.11.2010    source источник
comment
Чтобы передать аргументы тестируемой функции без использования анонимной функции, попробуйте Function.bind: stackoverflow.com/a/13233194/294855   -  person Danyal Aytekin    schedule 05.11.2012


Ответы (9)


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

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

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

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

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

person Pete Hodgson    schedule 10.11.2010
comment
Если вам тоже не нужно передавать аргументы, вы также можете просто передать ожидаемую функцию: expect(parser.parse).toThrow(...) - person SubmittedDenied; 01.11.2012
comment
Полезный совет: вы можете просто позвонить expect(blah).toThrow(). Отсутствие аргументов означает проверку, чтобы убедиться, что он вообще бросает. Сопоставление строк не требуется. См. Также: stackoverflow.com/a/9525172/1804678 - person Jess; 27.11.2013
comment
На мой взгляд, цель теста более очевидна при использовании анонимной функции. Кроме того, он остается неизменным для всех тестов, когда, например, вам нужно передать параметры целевой функции, чтобы она сгенерировала. - person Beez; 08.08.2014
comment
@SubmittedDenied: это вообще не работает! Если parser.parse использует this, передача его без контекста приведет к неожиданным результатам. Вы можете передать parser.parse.bind(parser), но, честно говоря ... анонимная функция была бы более элегантной. - person mhelvens; 12.01.2015

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

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"
}
person Andrzej Śliwa    schedule 10.11.2010
comment
На самом деле, глядя на код, toThrow с радостью принимает либо объект исключения /, либо / строку. Посмотрите, например, на какие вызовы он отправляет ожидаемое сообщение. - person Pete Hodgson; 10.11.2010
comment
Он позволяет использовать строку как побочный эффект строки, не имеющей свойства сообщения. - person mpapis; 10.11.2010
comment
Если вы выбрасываете объект, а не ошибку (как в вашем примере внизу), вы не получите трассировку стека в браузерах, которые его поддерживают. - person kybernetikos; 07.03.2012
comment
@kybernetikos удивительно, но не совсем верно; вы все равно получите трассировку стека, напечатанную в консоли Chrome, если вы выберете не Error (jsfiddle.net/k1mxey8j < / а>). Однако ваш брошенный объект, конечно, не будет иметь свойства .stack, что может быть важно, если вы хотите настроить автоматические отчеты об ошибках. - person Mark Amery; 14.02.2015

Как упоминалось ранее, функция должна быть передана в 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();

or

// I just want to know that an error of 
// a given type was thrown and nothing more
expect(() => parser.parse(raw))
  .toThrowErrorOfType(TypeError);
person Jamie Mason    schedule 24.01.2017
comment
Это expect(foo).toThrowError(TypeError); в Jasmine 2.5: jasmine.github.io/2.5/introduction - person Benny Neugebauer; 17.02.2017

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

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;
};
person Jake    schedule 28.07.2011

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

Вместо того:

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

Рассмотреть возможность:

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

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

person Jonathan Gawrych    schedule 05.02.2015

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

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

Для любителей кофе

expect( => someMethodCall(arg1, arg2)).toThrow()
person fernandohur    schedule 15.02.2014

Для тех, кто все еще может столкнуться с этой проблемой, для меня опубликованное решение не сработало, и оно продолжало выдавать эту ошибку: 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');
                });
            }));
person arifaBatool    schedule 15.02.2018
comment
Спасибо за это. Я был очень сбит с толку, но ваш комментарий имеет смысл. Я исправил проблему с помощью нового expectAsync jasmine.github.io/api/3.3/ async-matchers.html - person Benjamin; 28.08.2019

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

await expectAsync(asyncFunction()).toBeRejected();
await expectAsync(asyncFunction()).toBeRejectedWithError(...);
person Wildhammer    schedule 23.07.2020