2010-11-10 7 views
354

Estoy tratando de escribir una prueba para el Jasmine Test Framework que espera un error. Por el momento estoy usando un Jasmine Node.js integration from GitHub.¿Cómo escribir una prueba que espera que se genere un error en Jasmine?

En mi módulo Nodo Tengo el siguiente código:

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

Ahora trato de escribir una prueba que espera que este error:

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

Probé también Error() y algunas otras variantes y justo no puede encontrar la manera de hacerlo funcionar.

+3

para pasar argumentos a la función que se está probando, sin necesidad de utilizar una función anónima, tratan 'Function.bind': http://stackoverflow.com/ a/13233194/294855 –

Respuesta

628

que debería pasar una función en la llamada expect(...). El código que tenemos aquí:

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

está tratando de realidad llamadaparser.parse(raw) en un intento de pasar el resultado en expect(...),

Trate de usar una función anónima en su lugar:

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

Si no necesita pasar argumentos también, también puede pasar la función para esperar: 'expect (parser.parse) .toThrow (...)' – SubmittedDenied

+47

Consejo útil: Simplemente puede llamar a 'expect (blah) .toThrow() '. Sin argumentos significa verificar que no arroje nada. No se requiere coincidencia de cadenas. Ver también: http://stackoverflow.com/a/9525172/1804678 – Jess

+1

En mi opinión, es más obvio en cuanto a la intención de la prueba cuando se envuelve en una función anónima. Además, permanece constante entre todas las pruebas cuando, por ejemplo, tiene que pasar parámetros a la función objetivo para hacer que se lance. – Beez

55

Está utilizando:

expect(fn).toThrow(e) 

Pero si vas a tener una mirada sobre el comentario de función (es de esperar cadena):

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

supongo que probablemente debería escribirlo así (usando lambda - función anónima):

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

Esto se confirma en el ejemplo siguiente:

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

Douglas Crockford recomienda firmemente este enfoque, en lugar de utilizar "throw new Error()" (modo de prototipos):

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

En realidad, al mirar el código toThrow, tomaré felizmente un objeto de excepción/o/una cadena. Echa un vistazo a las llamadas que está haciendo a expected.message, por ejemplo. –

+1

Se junta para permitir la secuencia como un efecto secundario de cadena que no tiene propiedad de mensaje – mpapis

+1

Muchas gracias que funcionó. Todavía acepté la respuesta de Pete, porque su respuesta me lo dejó claro, que tenía que usar una lambda. Todavía +1 :-) ¡Gracias! – echox

22

I reemplace Jasmine's toThrow matcher con lo siguiente, que le permite coincidir con la propiedad de nombre de la excepción o su propiedad de mensaje. Para mí esto hace que las pruebas más fáciles de escribir y menos frágil, ya que puedo hacer lo siguiente:

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

y luego probar con lo siguiente:

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

Esto me permite modificar el mensaje de excepción más adelante sin romper pruebas, cuando lo importante es que lanzó el tipo esperado de excepción.

Esto es el equivalente de toThrow que permite esto:

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

Un buen enfoque, pero es {name: '...', mensaje: '...'} un objeto de error adecuado en JavaScript? – Marc

+1

Buen comentario @Marc. Tienes razón, la propiedad del nombre no es estándar. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error, pero ¿está tan equivocado? – Jess

+3

@Jake! ¡Encontré una mejor manera! Simplemente puede llamar a 'expect (blah) .toThrow()'. Sin argumentos significa verificar que no arroje nada. No se requiere coincidencia de cadenas. Ver también: http://stackoverflow.com/a/9525172/1804678 – Jess

5

Para los amantes CoffeeScript

expect(=> someMethodCall(arg1, arg2)).toThrow() 
7

sé que es más código pero también se puede hacer:

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

Tiendo a gustarle el aspecto de 'autodocumentación' ... hace que sea muy evidente que está probando un estado de error – JRulle

19

Una solución más elegante que crear una función anónima cuyo único propósito es envolver a otra, es usarde esfunción. La función de vinculación crea una nueva función que, cuando se llama, tiene su palabra clave this establecida en el valor proporcionado, con una secuencia dada de argumentos que precede a cualquiera que se haya proporcionado cuando se invoca la nueva función.

En lugar de:

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

considerar:

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

La sintaxis se unen permite probar las funciones con diferentes this valores, y en mi opinión hace que la prueba sea más legible. Ver también: https://stackoverflow.com/a/13233194/1248889

6

Como se mencionó anteriormente, una función que debe transmitirse a toThrow ya que es la función que usted describe en su ensayo: "espero que esta función para tirar x"

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

Si usando Jasmine-Matchers también puede usar uno de los siguientes cuando se adapten a la situación;

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

o

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

Es 'expect (foo) .toThrowError (TypeError);' en Jasmine 2.5: https://jasmine.github.io/2.5/introduction –

0

Para cualquier persona que todavía podrían enfrentarse a este problema, para mí la solución publicado no funcionaba y se mantuvo en lanzar este error: Error: Expected function to throw an exception. que más tarde se dio cuenta de que la función que esperaba arrojar un error era una función asíncrona y esperaba que se rechazara la promesa y luego lanzar error y eso es lo que estaba haciendo en mi código:

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

y eso es lo que hice en mi prueba y funcionó:

it('Test should throw error if request not found', willResolve(() => { 
     const promise = service.getRequestStatus('some-id'); 
       return expectToReject(promise).then((err) => { 
        expect(err.message).toEqual('REQUEST NOT FOUND'); 
       }); 
      })); 
Cuestiones relacionadas