Agora que você tem um ambiente de teste com o Testem configurado, você precisa começar a escrever testes. E é aí que as pessoas começam a se sentir intimidadas. “Ah, não, eu preciso aprender outra biblioteca” vem à mente de muitas pessoas, e por essa biblioteca não adicionar quaisquer recursos interessantes a seu site ou aplicativo, ela acaba não parecendo muito divertida. Mas nós precisamos deixar isso de lado, porque as bibliotecas de testes tendem a ter APIs muito, muito simples e podem ser aprendidas dentro de algumas horas de ajustes. Deixe-me provar isso a você.
Suítes e especificações
O Jasmine possui algumas funções globais principais em seu arsenal. Essas funções são globais principalmente porque o código é mais simples de ler. Você pode realmente lê-lo quase que como uma sentença. A primeira função sobre a qual vamos falar é describe. Ela função é usada para organizar seus testes em suítes. Uma suíte é apenas um nome fantasia para uma coleção de testes de modo que você possa organizá-los em blocos relacionados. Veja como é describe em ação:
describe("A description or subject for your test suite", function(){ // … Write your tests here. });
Como você pode ver ele tem dois argumentos: uma string que é usada para identificar uma suíte, e uma função que contém o código de teste real. A string é usada na visualização dos testes para mostrar hierarquicamente quais testes passaram e os que falharam. A função pode conter qualquer código que você quiser; no entanto, você precisa utilizar funções específicas fornecidas pelo Jasmine, a fim de obter qualquer coisa para aparecer nos resultados do teste, como explicaremos a seguir.
Suítes também podem ser aninhadas umas dentro das outras. Isso permite que você tenha uma organização de código refinada em estruturas hierárquicas. Eu costumo ter um bloco inicial describe para um objeto e então tiver aninhado blocos describe para cada um de seus métodos, assim:
describe("Some Object or 'class'", function(){ // … some tests describe("#methodName", function(){ // … tests related to this method }); describe("#anotherMethod", function(){ // … tests related to this method }); });
Agora que temos o código organizado em suítes, que geralmente representam os substantivos, é preciso escrever alguns testes (ou seja, especificações), que são os verbos. Fazemos isso com it. Ele é assim:
describe("This thing", function () { it("is awesome", function () { // … Do a bit of setup // … Check to see if it really 'is awesome' }); });
Como você pode ver, it está aninhado dentro de um bloco describe, de modo que a especificação do verbo possa ser associada a um substantivo que faça um commit no verbo. Dessa forma, describe é onde você diz que está o objeto que faz algo, e it é onde você diz o que ele faz. Dentro de it é onde realmente testa para verificar se o verbo foi concluído com êxito. Vamos discutir como fazer isso mais adiante.
Antes de ver isso, no entanto, dê uma olhada nos comentários que eu coloquei dentro do bloco it. Eu separei em duas seções diferentes: configuração e verificação. Primeiro você configurar e executar as funções necessárias. Então você testa para verificar se tudo funcionou do jeito que deveria. Essa é a forma “padrão” de fazer um teste e é o padrão que você deve sempre seguir. Obviamente, porém, se não tem nada para configurar, basta pular essa parte e começar a verificar.
Como usar expect enquanto você espera
Como eu disse há pouco, é dentro do it que você faz o seu teste real. Essencialmente, você está apenas verificando se o resultado é o mesmo que esperava. Jasmine usa expect como o nome da função que é utilizada para testar os resultados esperados. expect recebe um único argumento, que pode ser qualquer valor, e então retorna um objeto que tem um monte de métodos chamados matchers. Matchers é o que testa o valor para determinar se estava correto. É difícil explicar isso sem código, então dê uma olhada:
describe("This thing", function () { it("is awesome", function () { var returnValue = 1 + 1; expect(returnValue).toBe(2); }); });
Passamos returnValue para expect e isso nos deu um objeto com os métodos matcher nele. Escolhemos utilizar o matcher toBe e passou em dois. toBe apenas compara o valor dado para expect com o valor dado a ele usando ===. Essencialmente, isto é o que está acontecendo em Jasmine nessa situação:
if ( returnValue === 2) { // report the test passed } else { // report the test failed }
Se você não usa o expect e um matcher, os testes serão sempre considerados ensaios, a menos que haja um erro lançado (há pelo menos uma exceção, que eu vou discutir depois). Se você realmente quer ter certeza de que seus testes estão passando/falhando, então você precisa usar expect. Você pode usá-lo quantas vezes quiser por bloco it, mas deve tentar mantê-los mínimos. Se você precisa chamar expect muitas vezes, isso significa, provavelmente, que as funções que está testando estão fazendo muitas coisas. Se algum dos expect falhar, a especificação inteira falhará.
Há uma tonelada de matchers, e não vale a pena ver todos eles aqui. O toBe é muito comum é definitivamente um dos mais fáceis de entender e pode ser usado na maioria das situações, mas você deve consultar a documentação do Jasmine para ver o resto dos matchers. Você também pode criar matchers personalizados, mas não vou falar disso aqui. Eles só permitem que você escreva matchers que simplificam a forma como você escreve seus testes, de modo que eles são específicos para o seu domínio.
Instalação e limpeza
Mais duas funções que o Jasmine oferece são beforeEach e afterEach. Elas não são necessárias, mas podem ajudar a manter o seu código DRY. Você as usa dentro de seus blocos describe e antes de seus blocos it. Cada um deles pega uma função como seu único parâmetro, e essas funções são executadas antes/depois de cada uma das especificações, incluindo as especificações aninhadas mais profundamente nos blocos describe. Dessa forma, se você tem alguma configuração comum ou procedimentos de desmontagem, você pode colocá-los dentro de uma dessas funções e apenas escrevê-los uma vez, em vez de repeti-los dentro de cada um de seus blocos it. Aqui está um exemplo:
describe("This thing", function () { beforeEach(function(){ // Run some setup, like creating new objects }); afterEach(function(){ // Run some cleanup like disconnecting WebSockets }); it("is awesome", function () { // beforeEach is run before this var returnValue = 1 + 1; expect(returnValue).toBe(2); // afterEach is run after this }); it("makes 'cool' look like a hot summer day", function () { // beforeEach is run before this var returnValue = getSomeValue(); expect(returnValue).toBe(1); // afterEach is run after this }); });
O que é this
A última coisa sobre a qual vamos falar hoje é a palavra-chave this. Você pode usar closures e variáveis locais para conter todos os seus dados que serão repassados para cada uma de suas funções, mas esse não é o único caminho. Cada função que você passar (como a beforeEach, it e outras) será executada no mesmo contexto. Isso significa que você pode definir algo em beforeEach com this.someObj = … e é possível acessar esse objeto dentro de it com this.someObj. Aí vai de você qual técnica usar, mas eu ainda queria que você visse as opções para ter uma escolha.
describe("Some Object", function () { beforeEach(function(){ this.someObj = new SomeObject(); this.someObj.start(); }); afterEach(function(){ this.someObj.stop(); }); it("increments", function () { this.someObj.increment(); expect(this.someObj.value).toBe(1); }); describe("#aMethod", function(){ it("does stuff", function () { var value = this.someObj.aMethod(); expect(value).toBe("returned string"); }); }); });
Conclusão
Por hoje é só, pessoal. Na próxima semana, vamos discutir mais algumas coisas que Jasmine nos oferece, como Spies, jasmine.any e testes assíncronos.
***
Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.joezimjs.com/javascript/javascript-unit-testing-with-jasmine-part-1/