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


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.