javascript - Hur kan jag skriva ett test som förväntar sig att ett 'Error' kastas i Jasmine?


Jag försöker skriva ett test för Jasmine Test Framework som förväntar sig ett fel. För närvarande använder jag en Jasmine Node.js-integration från GitHub.

I mitt Node.js-modul har jag följande kod:

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

Nu försöker jag skriva ett test som förväntar sig detta fel:

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

Jag försökte också Error() och några andra varianter och kan bara inte lista ut hur jag ska få det att fungera.

Svar #1

Försök att använda en anonym funktion istället:

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

Eller använda en lambda:

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

du bör skicka en funktion till expect(...)-anropet. Din felaktiga kod:

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

försöker faktiskt anropa parser.parse(raw) i ett försök att skicka resultatet till expect(...),

Svar #2

Du använder:

expect(fn).toThrow(e)

Men om du tittar på funktionskommentaren (förväntat är sträng):

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

Jag antar att du förmodligen borde skriva det så här (med lambda - anonym funktion):

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

Detta bekräftas i följande exempel:

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

Douglas Crockford rekommenderar starkt detta tillvägagångssätt, istället för att använda "throw new Error()" (prototypreringssätt):

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

Svar #3

Som nämnts tidigare behöver en funktion skickas till toThrow eftersom det är den funktionen du beskriver i ditt test: "Jag förväntar mig att denna funktion kastar x"

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

Om du använder Jasmine-Matchers kan du också använda en av följande när de passar situationen;

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

eller

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

Svar #4

En mer elegant lösning än att skapa en anonym funktion vars enda syfte är att omsluta en annan, är att använda ES5:s bind-funktion. Bind-funktionen skapar en ny funktion som, när den anropas, har sitt this-nyckelord inställt på det angivna värdet, med en given sekvens av argument som föregår de som ges när den nya funktionen anropas.

Istället för:

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

Överväg:

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

Syntaxen bind låter dig testa funktioner med olika this-värden, och enligt min mening gör testet mer läsbart. Se även:

Kraver Jasmines toThrow-matchare att argumentet omsluts av en anonym funktion?

Svar #5

Jag ersätter Jasmines toThrow-matcher med följande, vilket låter dig matcha på undantagets name-egenskap eller dess message-egenskap. För mig gör detta tester lättare att skriva och mindre känsliga, eftersom jag kan göra följande:

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

och testa sedan med följande:

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

Detta låter mig justera undantagsmeddelandet senare utan att bryta testerna, när det viktiga är att det kastade den förväntade typen av undantag.

Detta är ersättningen för toThrow som tillåter detta:

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

Svar #6

Jag vet att det är mer kod, men du kan också göra:

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

Svar #7

I mitt fall var funktionen som kastade ett fel async, så jag följde detta:

await expectAsync(asyncFunction()).toBeRejected();
await expectAsync(asyncFunction()).toBeRejectedWithError(...);

Relaterade artiklar