web-dev-qa-db-fra.com

Comment écrire un test qui s'attend à une erreur dans Jasmine?

J'essaye d'écrire un test pour le Jasmine Test Framework qui attend une erreur. Pour le moment, j'utilise une intégration intégration de Jasmine Node.js de GitHub .

Dans mon module Node, j'ai le code suivant:

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

Maintenant, j'essaie d'écrire un test qui attend cette erreur:

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

J'ai aussi essayé Error() et quelques autres variantes et je n'arrive pas à comprendre comment le faire fonctionner.

440
echox

vous devriez passer une fonction à l'appel expect(...). Le code que vous avez ici:

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

tente de réellement appelparser.parse(raw) pour tenter de transmettre le résultat à expect(...),

Essayez plutôt d’utiliser une fonction anonyme:

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

Vous utilisez:

expect(fn).toThrow(e)

Mais si vous jetez un oeil sur le commentaire de la fonction (attendez-vous à être une chaîne):

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) {

Je suppose que vous devriez probablement l'écrire comme ceci (en utilisant lambda - fonction anonyme):

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

Ceci est confirmé dans l'exemple suivant:

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

Douglas Crockford recommande fortement cette approche, au lieu d'utiliser "throw new Error ()" (méthode de prototypage):

throw {
   name: "Error",
   message: "Parsing is not possible"
}
64
Andrzej Śliwa

Je remplace l'attribut toThrow de Jasmine par ce qui suit, ce qui vous permet de faire correspondre la propriété name de l'exception ou sa propriété message. Pour moi, cela rend les tests plus faciles à écrire et moins fragiles, car je peux faire ce qui suit:

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

puis testez avec ce qui suit:

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

Cela me permet d’ajuster le message d’exception plus tard sans interrompre les tests, alors que l’important est qu’il lance le type d’exception attendu.

C'est le remplacement de toThrow qui permet ceci:

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;
};
22
Jake

Une solution plus élégante que la création d'une fonction anonyme dont le seul but est d'envelopper une autre, consiste à utiliser la fonction es5 (bind) . La fonction bind crée une nouvelle fonction dont le mot-clé this, lorsqu'il est appelé, est défini sur la valeur fournie, avec une séquence d'arguments donnée précédant tous les paramètres fournis lorsque la nouvelle fonction est appelée.

Au lieu de:

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

Considérer:

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

La syntaxe de liaison vous permet de tester des fonctions avec différentes valeurs this et, à mon avis, rend le test plus lisible. Voir aussi: https://stackoverflow.com/a/13233194/1248889

22
Jonathan Gawrych

Comme mentionné précédemment, une fonction doit être passée à toThrow car c'est la fonction que vous décrivez dans votre test: "Je m'attends à ce que cette fonction jette x"

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

Si vous utilisez Jasmine-Matchers , vous pouvez également utiliser l'une des méthodes suivantes lorsqu'elles conviennent à la situation.

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

ou

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

Je sais que c'est plus de code mais vous pouvez aussi faire:

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

Pour les amateurs de café

expect( => someMethodCall(arg1, arg2)).toThrow()
5
fernandohur

Pour tous ceux qui pourraient encore être confrontés à ce problème, pour moi la solution affichée ne fonctionnait pas et continuait à émettre cette erreur: Error: Expected function to throw an exception. j'ai réalisé par la suite que la fonction que je m'attendais à lancer était une fonction asynchrone. et m'attendais à ce que la promesse soit rejetée et ensuite jette une erreur et c'est ce que je faisais dans mon code:

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

et c’est ce que j’ai fait dans mon test et cela a fonctionné:

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');
                });
            }));
1
arifaBatool