Як написати тест, який очікує, що помилка буде кинута в Жасмін?


490

Я намагаюся написати тест для Jasmine Test Framework, який очікує помилки. На даний момент я використовую інтеграцію Jasmine Node.js від GitHub .

У моєму модулі "Вузол" у мене є такий код:

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
На мою думку, очевиднішим є намір тесту при загортанні в анонімну функцію. Крім того, він залишається послідовним серед усіх тестів, коли, наприклад, вам доведеться передати параметри цільовій функції, щоб змусити його кидати.
Без

10
@SubmittedDenied: Це взагалі не працює! Якщо parser.parseвикористовувати this, передача його без контексту призведе до несподіваних результатів. Можна пройти parser.parse.bind(parser), але чесно кажучи ... анонімна функція була б більш елегантною.
mhelvens

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 {
   name: "Error",
   message: "Parsing is not possible"
}

3
Насправді дивлячись на код toThrow із задоволенням візьме або об’єкт винятку / або / рядок. Ознайомтеся, наприклад, з дзвінками, які він телефонує на очікувані повідомлення
Піт Ходжсон

1
Це дозволяє зробити рядок як побічний ефект рядка, що не має властивості повідомлення
mpapis

1
Велике спасибі, що спрацювало. Я все-таки прийняв відповідь Піта, тому що його відповідь зрозуміла мені, що я маю використовувати лямбда. Ще +1 :-) Дякую!
ехокс

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

Синтаксис прив’язки дозволяє тестувати функції з різними thisзначеннями, і, на мою думку, робить тест більш читабельним. Дивіться також: https://stackoverflow.com/a/13233194/1248889


23

Я замінюю Jasmin's ToThrow Matcher на наступний, який дозволяє вам відповідати за властивостями імені виключення або його властивістю повідомлення. Для мене це робить тести легшими для написання та менш крихкими, оскільки я можу зробити наступне:

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. Ви маєте рацію, властивість імені не є стандартною. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… , але це так неправильно?
Джесс

4
@Jake! Я знайшов кращий спосіб !!!! Можна просто зателефонувати expect(blah).toThrow(). Жодних аргументів не означає перевірити, чи це взагалі кидає. Не потрібно відповідати рядки Дивіться також: stackoverflow.com/a/9525172/1804678
Джесс

5
Спасибі Джесс - це правда, але тоді це може бути кидання якоїсь іншої помилки, наприклад TypeError, і мій тест буде неправильно пройдений, маскуючи справжню помилку.
Джейк

4
Тепер ви також можете використовувати RegEx як аргумент для toThrow ().
Тоні О’Хаган

21

Як було сказано раніше, функцію потрібно передати, toThrowоскільки це функція, яку ви описуєте у своєму тесті: "Я очікую, що ця функція кине х"

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

Якщо ви використовуєте Жасмін-Матчер, ви також можете скористатись одним із наступних, коли вони відповідають ситуації;

// 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
It expect(foo).toThrowError(TypeError);in Jasmine 2.5: jasmine.github.io/2.5/introduction
Benny Neugebauer

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 вирішив
Бенджаміна
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.