Todo mundo sabe que o teste unitário é bom e que ele deve ser feito. Não é exatamente simples de começar, mas uma vez que você está no caminho, é muito simples e altamente valioso. Mas como é que você começa? Eu vou te mostrar uma configuração ideal para Jasmine para te preparar e executar com o teste unitário e fazê-lo com estilo com Testem. Testem e Jasmine se juntam para tornar testes unitários divertidos e simples.
O que é Testem?
Testem é uma ferramenta de linha de comando que funciona em Node.js (e o que não roda em Node.js hoje em dia?). Ele permite lançar testes unitários para praticamente qualquer framework direto da linha de comando. Esses testes são, então, executados em cada um dos navegadores que você especificou – seja por meio de configuração ou de argumentos na linha de comando.
Quando o Testem estiver rodando e os navegadores iniciados, toda vez que você salvar um arquivo, os navegadores (conhecidos como “launchers” pelo Testem) irão automaticamente atualizar todos os seus testes novamente. Não apenas os resultados dos testes são mostrados em cada launcher, o Testem fornece uma interface limpa e tabulada dentro do console para exibir os resultados retornados de cada launcher. Isso significa que você pode manter os browsers no background e ainda ver todos os resultados de todos os testes. Veja a imagem abaixo, que ilustra o que acabei de falar.
Essa interface é controlada com o teclado. Você pode usar as setas para a esquerda e direita para alternar entre as abas, e como a nota na parte inferior da tela acima diz, você pode clicar em “Enter” para executar novamente os testes ou pode sair com `q`. Você pode ler mais sobre os controles de teclado e tudo mais relacionado ao Testem em seu repositório no GitHub. Você também pode assistir a uma ótima introdução para Testem, cortesia de Jeffrey Way do Nettuts+, no site do Nettuts+.
Por agora, tudo o que você precisa realmente saber é que você pode instalar o Testem com o NPM: npm install testem –g. Agora você pode executar testem diretamente do console para iniciar os testes.
Organização: estrutura de arquivo e de pastas
Agora que temos o Testem instalado, precisamos conversar sobre como configurar o projeto para usar Testem e Jasmine. Eu realmente tenho um repositório no Github com o meu Jasmine-Testem Boilerplate. Você pode simplesmente clonar o repositório para começar. Mas vou explicar um pouco sobre isso aqui primeiro. Aqui está como a estrutura do arquivo principal se parece:
root |- js/ | |- lib/ // All third party scripts | |- models/ // Backbone models | |- routers/ // Backbone Routers | |- templates/ // Underscore/Handlebars templates | |- views/ // Backbone Views | |- app.js // Marionette application | |- config.js // RequireJS configuration | `- main.js // Main file for bootstrapping/starting the app |- test/ | |- helpers/ // Any objects/functions to help with testing | |- lib/ // Jasmine's library files | |- spec/ // All tests | |- index.html // HTML page to open in browser for running tests | `- spec-runner.js // loads tests and configures RequireJS `- testem.yml // Testem configuration
É possível ver algumas notas mencionando Backbone e RequireJS. Na verdade, esse boilerplate é projetado para me preparar e executar com um aplicativo Backbone.Marionette/TwitterBootstrap/RequireJS rapidamente e tê-lo pronto para testar desde o início. Algumas notas rápidas: /js/config.js e /test/spec-runner.js têm exatamente a mesma configuração RequireJS neles, exceto:
- spec-runner.js inclui alguns caminhos aliases especificamente para testar
- spec-runner.js especifica um baseUrl, que é o /js/ folder
Tudo que é relacionado ao teste está na pasta /test/, e tudo relacionado à aplicação real (pelo menos os arquivos e os templates JavaScript) está na pasta /js/. Você pode querer mudar essa estrutura de arquivos, mas você vai querer fazer algumas mudanças de configuração em spec-runner.js e testem.yml.
Configurando Jasmine
Precisamos configurar o arquivo HTML que executa os testes do Jasmine. Já está tudo configurado dentro do boilerplate que eu mencionei acima, porém eu gostaria de explicar algumas das escolhas e como as coisas funcionam.
Primeiro, Testem é capaz de ser configurado de uma maneira que ele vai criar seu próprio arquivo HTML para executar os testes apenas especificando onde estão os arquivos de origem JavaScript e os arquivos especificados. Existem duas razões para eu criar meu próprio arquivo HTML em vez de tomar esse caminho:
- Meus aplicativos utilizam RequireJS, e eu não conheço nenhuma outra forma de fazer com que o Testem use o RequireJS corretamente.
- Se temos o nosso próprio arquivo HTML, podemos executar os testes sem Testem. Isso é ótimo se um projeto for clonado para uma máquina que não tem Testem – ou mesmo Node.js – instalado. Eu organizo as coisas de modo que os testes serão muito bem executados com ou sem Testem.
Vamos dar uma olhada de perto no arquivo HTML para ver o que eu fiz:
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>Jasmine Spec Runner</title> <link rel="shortcut icon" type="image/png" href="lib/jasmine_favicon.png"> <link rel="stylesheet" type="text/css" href="lib/jasmine.css"> </head> <body> <!– sandbox that tests can use for things that require the DOM –> <div id="sandbox"></div> <script src="lib/jasmine.js"></script> <!– Pull in Testem helper when using Testem –> <script src="/testem.js"></script> <script src="lib/jasmine-html.js"></script> <!– Using RequireJS. Specs and config are done in spec-runner.js –> <script type="text/javascript" src="../js/lib/require.js" data-main="spec-runner"></script> </body> </html>
Em sua maioria, são apenas coisas normais. Se você observar jasmine.css, verá que está alterado a partir do CSS padrão. Acho que o meu tem uma aparência mais agradável, e também oculta todas as especificações individuais – exceto as que falharam. Se você passar o cursor sobre uma suíte, todas as suas especificações deslizam para baixo para revelar toda a lista. Isso encolhe consideravelmente a quantidade de espaço necessária para listar as especificações.
Em primeiro lugar, existe um elemento “sandbox”. Se um teste unitário requer o uso do DOM, certifique-se de que ele seja feito aqui e que seja limpo quando você terminar. Em seguida, puxe o arquivo principal jasmine.js, testem.js e depois jasmim-html.js. Esses três arquivos são carregados fora dos mecanismos do RequireJS por algumas razões.
- A ordem importa. RequireJS vai executar os arquivos dependentes na ordem em que estão terminando de fazer download, e não na ordem especificada no array, por isso precisamos garantir que os temos na ordem correta.
- testem.js não está sempre disponível. Esse arquivo é disponibilizado somente quando Testem está executando os testes e reporta os resultados entre o navegador e o console. Se tentássemos carregar o testem.js via RequireJS, e abríssemos o arquivo HTML diretamente no navegador sem o Testem, ele quebraria e os testes não rodariam.
Em seguida, carregue o require.js, que, devido ao atributo data-main da tag script, carrega spec-runner.js. Então, vamos dar uma olhada em spec-runner.js.
require.config({ baseUrl: "../js", urlArgs: 'cb=' + Math.random(), paths: { // Libraries. jquery: "lib/jquery", underscore: "lib/lodash", backbone: "lib/backbone", marionette: "lib/backbone.marionette", // Marionette's extra dependencies "backbone.babysitter": "lib/backbone.babysitter", "backbone.eventbinder": "lib/backbone.eventbinder", "backbone.wreqr": "lib/backbone.wreqr", // RequireJS Plugins tpl: "lib/require.tpl", // jQuery Plugins bootstrap: "lib/bootstrap", // Jasmine Testing: Folder Aliases spec: "../test/spec", helpers: "../test/helpers" }, shim: { 'backbone': { deps: ["jquery", "underscore"], exports: "Backbone" }, 'bootstrap': ['jquery'] } }); require([], function(){ var jasmineEnv = jasmine.getEnv(); var htmlReporter = new jasmine.HtmlReporter(); jasmineEnv.addReporter(htmlReporter); // Add links to the spec files here var specs = []; specs.push('spec/example_spec'); // Execute specs require(specs, function(){ jasmineEnv.execute(); }); });
Tem muita coisa para ver aqui. No topo, estamos configurando RequireJS. Você vai perceber que vamos definir o baseUrl no diretório /js/, então os arquivos de origem vão funcionar da mesma forma que antes. Além disso, nós adicionamos urlArgs lá para adicionar uma string de consulta aleatória, de modo que nós não executamos em todos os arquivos armazenados em cache. O resto da configuração é caminho normal para as bibliotecas e corresponde ao arquivo config.js encontrado na pasta js, exceto que você vai ver na parte inferior de paths que eu também adicionei a especificação e pastas de helpers, de modo que é mais fácil para conduzi-los.
Após a configuração, começamos a configurar o Jasmine da mesma maneira que você normalmente faria. Então nós temos a array specs. Essa array contém todos os caminhos da string para todas as especificações que você deseja executar. Apenas continue executando push. Então, fazemos require nas especificações e as executamos.
Configurando Testem
Agora precisamos configurar Testem para que ele saiba quais arquivos carregar. Dê uma olhada no arquivo testem.yml no diretório raiz.
framework: jasmine src_files: - js/** - test/** test_page: test/index.html launch_in_dev: - Firefox
A primeira coisa que você vai notar é que eu estou usando o formato YML em vez do JSON. A única razão para isso é que eu estava tendo dificuldades em conseguir a configuração JSON para trabalhar na minha máquina Windows. Parece ter algo a ver com salvar para UTF-8 ou algo assim. Em todo caso, quando Testem estava lendo o arquivo de configuração, encontrou alguns caracteres extras que não estavam realmente no arquivo. Se eu usasse o Notepad, fizesse o arquivo .yml e explicitamente o salvasse como UTF-8 (em vez do padrão ANSI), funcionaria. Caso contrário, eu continuaria executando em problemas.
De qualquer forma, a primeira propriedade é framework. O padrão é Jasmine, então isso não é necessário, mas eu coloquei aqui de qualquer maneira. Em seguida, temos src_files. Isso permite que Testem saiba onde os arquivos de origem e especificação estão. Normalmente, ele só iria carregar todos esses arquivos (na ordem especificada, de modo que os arquivos js/estariam antes dos arquivos test/) e os colocaria no próprio arquivo HTML, mas já temos o nosso próprio arquivo HTML, como especificado pela propriedade seguinte: test_page, que só observa esses arquivos para mudanças para que possa executar novamente os testes.
Como eu disse antes, test_page permite ao Testem saber que estamos usando a nossa própria página HTML e o aponta para onde ele está. Finalmente, temos launch_in_dev, que controla quais launchers são usados quando você executar Testem em seu ambiente “dev”. Confira os documentos, caso queira saber mais sobre esses launchers e execução de testes em ambientes que não apenas “dev”. Eu estou usando apenas o Firefox aqui, mas é fácil mudar para o navegador no qual você quer desenvolver.
Execute-o
Agora que vem a parte divertida: nós começamos a executar Testem. No seu console, vá para o diretório raiz e digite testem. Se você estiver usando o boilerplate, você deve ver uma suíte de teste muito curta. Faça algumas alterações em qualquer um dos arquivos nas pastas /js/ ou /test/, e os testes devem ser atualizados automaticamente para você também!
Conclusão
Testem pode tornar os testes muito mais divertidos, mas você ainda tem que configurá-lo primeiro. Espero que isso tenha ajudado um pouco e que tenha, quem sabe, conseguido mais gente para o grupo de testes unitários.
***
Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.joezimjs.com/javascript/setting-up-a-jasmine-unit-testing-environment-with-testem/