Jasmine: entendendo os matchers

Provavelmente você já deve ter ouvido de Jasmine certo? Por acaso, você conhece todos os matchers que ele nos oferece pra testar?

por Raphael Fabeni 30/03/2015

Uma parte legal do Jasmine e que adianta muito o nosso lado são os matchers: de um modo resumido e direto, um _matcher_ implementa uma comparação _booleana_ entre o valor atual e o valor esperado. É responsável em passar para o _framework_ se o que  esperamos através do nosso teste é _verdadeiro_ ou falso. Com base nisso, o _Jasmine_ vai passar ou falhar a spec.

toEqual

Esse talvez seja o mais básico e um dos que mais iremos usar. Simplemente verifica se duas coisas são iguais (e não necessariamente o mesmo objeto). Por exemplo, as seguintes _expects_ iriam passar:

expect(true).toEqual(true);
expect([1, 2, 3]).toEqual([1, 2, 3]);

Da mesma forma, as seguintes iriam falhar:

expect(5).toEqual(12);
expect([1, 2, 3]).toEqual([11, 12, 13]);

toBe

O _matcher_ toBe a princípio parece ser igual ao anterior toEqual. A diferença é que toBe verifica não só se os dois valores são iguais, mas também como se eles são do mesmo objeto.

Pra podermos ver a diferença entre os dois:

var bob = { model: "Camaro" };
var john = { model: "Camaro" };

expect(bob).toEqual(john); // passa => são equivalentes
expect(bob).toBe(john); // falha => não é o mesmo objeto

Apesar de bob e john serem similares, eles não são o mesmo objeto, o que faz a spec passar se for usado o matcher toEqual mas falha se for usado o matcher toBe. O mesmo acontece para arrays:

var group = [100, 101, 102];
expect(group).toEqual([100, 101, 102]); // passa => são equivalentes
expect(group).toBe([100, 101, 102]); // falha => não é o mesmo array

toBeTruthy e toBeFalsy

Para testar se algum valor é avaliado commo true ou false, podemos usar respectivamente os _matchers_ toBeTruthy e toBeFalsy:

expect(true).toBeTruthy();
expect(1000).toBeTruthy();
expect({}).toBeTruthy();

expect("").toBeFalsy();
expect(null).toBeFalsy();
expect(false).toBeFalsy();

Se pararmos pra olhar com calma o exemplo anterior podemos notar que a avaliação dos _matchers_ toBeTruthy e toBeFalsy é idêntica ao JavaScript. Então temos alguns valores específicos que são considerados _falsy_ e todo o restante é avaliado como truthy. Pra nossa referência, uma lista dos valores que são avaliados como falsy pelo Jasmine:

  • false

  • ****

  • “”

  • undefined

  • null

  • NaN

not

Muitas vezes podemos inverter um _matcher_  pra termos certeza de que ele não é um valor true. Podemos fazer isso facilmente adicionando o prefixo .not:

expect('Fabeni').not.toEqual('Finelli');

toContain

Conseguimos também verificar se um elemento _está contido_ em um _array_ ou string por exemplo, como o matcher toContain.

expect([10, 11, 12, 13, 14, 15]).toContain(13);
expect('Raphael Fabeni').toContain('Fabeni');

toBeDefined e toBeUndefined

Da mesma maneira que vimos os matchers toBeTruthy e toBeFalsy, _Jasmine_ também nos oferece os benditos toBeDefined e toBeUndefined que verificam se um valor é defined ou undefined.

var iAmUndefined;
expect(null).toBeDefined(); // passa
expect('Fabeni').toBeDefined(); // passa
expect(iAmUndefined).toBeDefined(); // falha

expect(iAmUndefined).toBeUndefined(); // passa
expect(12).toBeUndefined(); // falha
expect(null).toBeUndefined(); // falha

toBeNull

Direto ao ponto, esse brother simplesmente avalia se um valor é null:

expect(null).toBeNull(); // passa
expect(false).toBeNull(); // falha
expect(1).toBeNull(); // falha

toBeNaN

Sem muitas delongas, esse _matcher_ verifica se um valor é NaN:

expect(0).toBeNaN(); // falha
expect(10).not.toBeNaN(); // passa

toBeGreatherThan e toBeLessThan

Esses dois matchers verificam se um valor é maior ou menor que um outro valor passado.

expect(10).toBeGreatherThan(1); // passa
expect(10).toBeLessThan(20); // passa

toBeCloseTo

Esse _matcher_ permite que possamos verificar se um certo número está próximo de um outro número, dado uma certa precisão decimal como segundo argumento. Poderíamos por exemplo, verificar se um número é próximo de _25.23_ com um ponto decimal, poderíamos fazer algo assim:

expect(25.23).toBeCloseTo(25.2, 1); // passa

toMatch

Esse cara verifica se algum valor está de acordo com base em uma expressão regular.

expect('Yes, we can!').toMatch(/we/); // passa

toThrow

Esse _matcher_ permite que verifiquemos se uma função lançou um erro. Como exemplo, vamos imaginar que temos uma função onlyNumbers que deve lançar uma exceção caso o argumento passado seja uma _string_ e não um número. Podemos usar aqui uma _função anônima_ para nos facilitar a vida:

expect(function() {
    onlyNumbers('argumento errado')
}).toThrow();

Ufa…

Deu pra ver que o _framework_ nos oferece um monte de opção para utilizarmos em nossos testes. É ainda é possível fazer nossos matchers customizados, mas vou deixar isso para um próximo post. Se você se interessar mais pelo assunto, recomendo o livro JavaScript Testing with Jasmine que inclusive li recentemente e tive a idéia de escrever o post.

Acho que é isso. Se encontrar algum erro ou melhoria no post, pode postar no comentário ou pode me chamar no twitter.