jueves, 25 de junio de 2015

Jasmine. Matchers (III) - toThrow, toThrowError

Artículos anteriores:
Jasmine. Introducción. Creando el primer test
Jasmine. Matchers (I) - toBe, toEqual, toMatch, toBeDefined, toBeUndefined, toBeNull
Jasmine. Matchers (II) - toBeTruthy, toBeFalsy, toContain, toBeLessThan, toBeGreaterThan, toBeCloseTo

En este artículo voy a mostrar el funcionamiento de dos matchers de Jasmine un tanto especiales, se trata de matchers que, en lugar de comprobar que nuestro código no falla, nos ayudan a comprobar que nuestro código falla cuando debe de fallar y de la forma que debe de fallar.

Son los matchers toThrow y toThrowError que nos permiten comprobar que las funciones producen errores cuando esperamos que los produzcan, generalmente cuando reciben parámetros no válidos.

Para ver el funcionamiento de ambos matchers voy a crear en la carpeta scripts un archivo MathFunctions.js con una única (y simple) función diferencia que recibe dos parámetros y devuelve el resultado de restar el segundo parámetro del primero.

La función devuelve error en caso de que los parámetros no sean numéricos o si el primer parámetro es menor que el segundo. En el primer caso se devuelve un Error genérico de JavaScript con el correspondiente mensaje y, para el segundo, utilizo un tipo de error InvalidArgumentException.

function InvalidArgumentException(message) {
    this.name = "InvalidArgumentException";
    this.message = message;
}
InvalidArgumentException.prototype = new Error();
InvalidArgumentException.prototype.constructor = InvalidArgumentException;


function diferencia(valorInicial, valorFinal) {
    var ini = parseFloat(valorInicial);
    var fin = parseFloat(valorFinal);
    if (isNaN(ini) || isNaN(fin))
        throw new Error("Los valores inicial y final deben ser numéricos");

    if (ini > fin)
        throw new InvalidArgumentException("El valor inicial debe ser menor que el final");

    return fin - ini;
}

toThrow

El matcher toThrow comprueba que una función produce un error en determinadas circunstancias, por ejemplo si una función que espera un valor numérico recibe una cadena.

Por supuesto también podemos comprobar que la función no produce un error utilizando not.toThrow.

describe("toThrow", function () {

    it("Si no recibe parámetros se produce error", function () {
        expect(diferencia).toThrow();
    });

    it("Si los parámetros no son numéricos se produce error", function () {
        expect(diferencia.bind(null, "Asier", 45)).toThrow();
        expect(diferencia.bind(null, 34.4, "x0")).toThrow();
    });

    it("Si el valor inicial es mayor que el final se produce error", function () {
        expect(diferencia.bind(null, 10, 5)).toThrow();
        expect(diferencia.bind(null, 10.01, 10)).toThrow();
    });

    it("Si son números y el valor final mayor o igual que el inicial no hay error", function () {
        expect(diferencia.bind(null, 0, 0)).not.toThrow();
        expect(diferencia.bind(null, 4, 10)).not.toThrow();
        expect(diferencia.bind(null, "123", "1110")).not.toThrow();
        expect(diferencia.bind(null, 10, 10.01)).not.toThrow();
    });

});

Resultado tests toThrow

toThrowError


El matcher toThrowError no sólo comprueba si se produce un error, si no que además valida que el error sea el esperado. Permite comprobar dos datos del error devuelto: el mensaje del error y el tipo.

El matcher puede llamarse con uno o dos parámetros. En caso de utilizar un único parámetro el funcionamiento del matcher depende del tipo del valor de éste:
  • si es una cadena: comprueba que la cadena se corresponde con el mensaje de error devuelto
  • si es una expresión regular: comprueba que el mensaje de error cumple la expresión regular
  • si es un tipo de objeto: comprueba que el error devuelto es del tipo especificado
En caso de utilizar dos parámetros el primero será el tipo de error y el segundo una cadena o expresión regular para validar el mensaje.

describe("toThrowError", function () {

    it("El error devuelve el mensaje de error esperado", function () {
        expect(diferencia).toThrowError("Los valores inicial y final deben ser numéricos");
        expect(diferencia).toThrowError(/deben ser numéricos/);
        expect(diferencia).not.toThrowError("El valor inicial debe ser menor que el final");
        expect(diferencia.bind(null, 10, 5))
            .toThrowError("El valor inicial debe ser menor que el final");
    });

    it("El error es del tipo esperado", function () {
        expect(diferencia.bind(null, "Asier", 45)).toThrowError(Error);
        expect(diferencia.bind(null, "Asier", "Villanueva"))
            .not.toThrowError(InvalidArgumentException);
        expect(diferencia.bind(null, 10, 5)).toThrowError(Error);
        expect(diferencia.bind(null, 10, 5)).toThrowError(InvalidArgumentException);
        expect(diferencia.bind(null, 5, 10)).not.toThrowError(Error);
        expect(diferencia.bind(null, 5, 10)).not.toThrowError(InvalidArgumentException);
    });

    it("El mensaje y el tipo del error son los esperados", function () {
        expect(diferencia.bind(null, 34.4, "x0")).toThrowError(Error, /deben ser numéricos/);
        expect(diferencia.bind(null, 10, 5))
            .toThrowError(InvalidArgumentException, "El valor inicial debe ser menor que el final");
        expect(diferencia.bind(null, 10, 5))
            .not.toThrowError(InvalidArgumentException, /deben ser numéricos/);
    });

});

Resultado tests toThrowError


No hay comentarios:

Publicar un comentario