: : www.mundoj.com.br : :
Caíres Vinicius
([email protected]): é desenvolvedor e consultor pela
Caelum nas linguagens em Java, Ruby e Javascript. Entusiasta de
usabilidade para Web e Javascript Server-side.
Lucas Souza
([email protected]): é bacharel em Engenharia da
Computação pela Universidade de Ribeirão Preto, possui a
certificação SCJP e trabalha com Java há 4 anos. Atualmente é
desenvolvedor e instrutor pela Caelum. Entusiasta de metodologias
ágeis e editor-chefe do InfoQ Brasil.
Testando seus códigos
JavaScript: A importância e a
facilidade de criá-los
Aprenda como escrever testes de unidade para o lado
cliente da sua aplicação
A linguagem JavaScript tem se tornado a principal
nas aplicações conhecidas como rich applications ou
aplicações web 2.0. Porém ainda não tem recebido a
devida atenção quando o assunto diz respeito a testes
automatizados. No artigo será mostrado na prática
a importância e a relevância de se criar testes para
seus códigos JavaScript, mostrando ferramentas que
permitem a criação de simples testes de unidade, até a
criação de mocks simulando dependências complexas.
ódigo JavaScript tem se tornado cada vez mais frequente
em aplicações web ricas, as ditas aplicações web 2.0.
Bibliotecas como jQuery, Prototype, Mootools e outras
facilitam muito o trabalho dos desenvolvedores. Porém o código
muitas vezes acaba não recebendo a atenção necessária, e os problemas são descobertos ou pelos usuários das aplicações ou pelos
desenvolvedores quando utilizam ferramentas de debugging no
estilo do Firebug. Mesmo com essas ferramentas e com a correção
de erros sendo feita frequentemente, não temos muito cuidado
no processo de isolar determinados códigos assim como é feito
quando nos referimos a códigos no server side.
O código termina como uma big ball of mud, ou uma grande
“bola de lama”, onde misturamos códigos que manipulam a árvore DOM, código de chamadas Ajax para o servidor. Quando
46
precisamos alterar estes códigos temos uma dificuldade imensa.
Em relação a nossa aplicação, a primeira opção prioriza a funcionalidade, isto é, deve funcionar com ou sem javascript. Essa
solução é conhecida como Graceful Degradation. Garantir que a
aplicação funcione sem javascript vai gerar uma perda de usabilidade.
O objetivo do artigo é mostrar que testes em códigos JavaScript
são fáceis de fazer e hoje em dia existem muitas ferramentas que
nos auxiliam em processos complexos, como a criação de mocks e
stubs, além da integração destes testes com servidores de integração contínua. Com isso, obtemos códigos mais legíveis e coesos e,
evidentemente, com menor número de bugs.
Testes de unidade com QUnit
Vamos primeiro simular um cenário no qual devemos calcular
alguns impostos e mostrar o resultado para o usuário. Poderíamos
resolver o problema de duas maneiras:
•Uma requisição no server side para calcular nosso imposto e retornar a página preenchida com o valor.
•Um código JavaScript que calcula o valor do imposto, sem nenhuma requisição feita no server side.
Pensando em cada vez mais melhorar a usuabilidade e a experiência do usuário em relação a nossa aplicação, ou seja, pensando
na questão do graceful degradation, a primeira opção é mais recomendada. Porém, é uma solução que possui um problema, que
pode ser uma resposta lenta ao usuário.
Neste caso, fazendo o cálculo através de um código JavaScript
deixaria a aplicação mais rápida pelo fato de não ter que fazer
uma requisição e esperar a resposta do servidor, evitando também
tráfegos desnecessários pela rede.
Quando não testamos, perdemos qualidade. Com JavaScript não
é diferente, e se não testarmos, podemos colocar um valor errado
que pode prejudicar a parte financeira que utiliza sua aplicação
ou não exibir um elemento HTML que deveria aparecer e não
aparece. O teste é também para garantir essa qualidade.
Em nosso primeiro código, adotaremos a abordagem de TestDriven Development, que é uma excelente prática de desenvolvimento, e que já é seguida fortemente quando testamos códigos
server side. Vamos começar pela criação de um teste e depois
escreveremos o código javascript para que este teste passe.
Utilizaremos uma ferramenta de testes chamada QUnit, que é
muito semelhante ao JUnit utilizado para testes de unidade em
Java. O QUnit é a ferramenta escolhida pelo conhecido projeto
JQuery para efetuar os testes da sua implementação e também dos
plugins existentes para ele. Mas também pode ser utilizado para
testar códigos JavaScript que não utilizam JQuery, exatamente o
que confere com o nosso cenário.
Para criarmos nosso teste utilizando o Qunit, basta escrever o
HTML da Listagem 1, e dentro da tag <script> colocar a função onload. Dentro da função onload, ficam as asserções que desejamos
efetuar. Um detalhe é que neste arquivo incluiremos o arquivo
impostos.js, que contém a função que desejamos testar. Também
está incluso a importação de um arquivo css do próprio QUnit
para fazer a formatação do HTML retornado. Na tag <body> são
incluídos alguns elementos HTML onde o QUnit exibirá os resultados dos nossos testes.
Nosso teste falha porque ainda não implementamos a função
"calcula". Para calcular o valor com os impostos precisamos de
três informações: valor, valor do imposto e uma taxa administrativa. No nosso caso, vamos fazer uma multiplicação e uma
adição como fórmula para o cálculo do nosso imposto. Sabendo
quais argumentos e qual a fórmula do cálculo do imposto, vamos
implementar nossa função dentro do arquivo impostos.js, assim
como feito na Listagem 2.
Listagem 1. Criação do teste utilizando Qunit.
<html>
<head>
<link rel=”stylesheet” href=”qunit.css” type=”text/css” media=”screen” />
<script type=”text/javascript” src=” qunit.js”></script>
<script type=”text/javascript” src=”impostos.js”></script>
<script>
window.onload = function() {
test(“calcula imposto corretamente”, function() {
equals(5.7, Imposto.calcula(10.20, 0.5, 0.6));
});
}
</script>
</head>
<body>
<h1 id=”qunit-header”>QUnit example</h1>
<h2 id=”qunit-banner”></h2>
<h2 id=”qunit-userAgent”></h2>
<ol id=”qunit-tests”></ol>
</body>
</html>
Listagem 2. Criação do cálculo de impostos.
Imposto.calcula = function(valor, imposto, taxa) {
return (valor * imposto) + taxa;
}
Se executarmos em nosso browser favorito, o código presente na
Listagem 1, perceberemos que ele nos retorna uma mensagem de
erro, dizendo que nosso código falhou. Isto ocorre por causa do
arredondamento do ponto flutuante do Javascript. Ao invés de
retornar 5.7, nossa função retorna o valor 5.699999999999999.
O que aconteceu no código acima é um problema que pode ser
considerado crônico em relação a arredondamentos em linguagens de programação, por exemplo, a linguagem Java. A linguagem JavaScript também tem problemas com arredondamento de
pontos flutuantes.
Percebemos este problema apenas porque nosso código Javascript
recebeu a devida atenção e criamos um teste de unidade para
um cálculo que parecia simples, mas que poderia causar grandes
transtornos.
Mas como podemos resolver este problema? Existe alguma solução nativa do JavaScript? JavaScript possui uma biblioteca
que faz algo similar ao BigDecimal: a classe BigNumber (http://
jsfromhell.com/classes/bignumber). Esta classe JavaScript resolve
nosso problema e faz nosso teste passar, garantindo uma melhor
47
: : www.mundoj.com.br : :
precisão em cálculos com pontos flutuantes. Basta alterarmos a
implementação do código presente na função calcula. O código
utilizando a classe BigNumber é mostrado na Listagem 3.
Listagem 3. Cálculo de impostos utilizando BigNumber.
Imposto.calcula = function(valor, imposto, taxa) {
var valorComImposto = new BigNumber(valor).
multiply(imposto);
return Number(new BigNumber(taxa).add(valorComImposto));
}
Utilizando a classe BigNumber e executando o teste novamente,
ele passará.
Porém existe outra maneira de corrigir nossa função utilizando o
método toFixed() do próprio Javascript, que formata um número
usando a notação de um ponto fixo. Utilizando o toFixed nosso
código ficaria como o descrito na Listagem 4.
Listagem 4. Cálculo de impostos utilizando toFixed.
Listagem 5. Mock de Ajax com ScrewUnit e Smoke.
<html>
<head>
<script src=”jquery-1.3.2.js”></script>
<script src=”jquery.fn.js”></script>
<script src=”jquery.print.js”></script>
<script src=”screw.builder.js”></script>
<script src=”screw.matchers.js”></script>
<script src=”screw.events.js”></script>
<script src=”screw.behaviors.js”></script>
<script src=”smoke.core.js”></script>
<script src=”smoke.mock.js”></script>
<script src=”smoke.stub.js”></script>
<script src=”smoke.ajax.js”></script>
<link rel=”stylesheet” href=”screw.css”>
<script type=”text/javascript”>
Screw.Unit(function() {
describe(“Faz um requisicao Ajax e retorna um html como
sucesso”, function() {
it(“mockando uma requisicao ajax com sucesso”, function() {
Smoke.Ajax.mock(“/meuRecurso”, “<p>Parabens!</p>”, 200);
var resultado, httpStatus;
$.ajax({
url:”/meuRecurso”,
success: function(data, textStatus){
resultado = data;
httpStatus = textStatus;
},
error: function(error){
resultado = error.responseText;
httpStatus = error.status;
}
Imposto.calcula = function(valor, imposto, taxa) {
return ((valor * imposto) + taxa).toFixed(2);
}
});
Testes de unidade com DOM, Ajax e Callbacks
utilizando Screw.Unit e Smoke
Bibliotecas como jQuery nos ajudam a manipular a árvore DOM e
principalmente a fazer requisições Ajax, que é a rotina mais comum
para a grande maioria dos desenvolvedores, mas mesmo sendo
muito corriqueira poucos testam e tomam o cuidado necessário.
Como testaríamos nossa requisição Ajax? Como verificamos se o
elemento HTML foi alterado? Fazendo uma requisição de verdade?
Para testes com Ajax temos a opção de criar objetos falsos, mais
conhecidos como mocks, que simulem a requisição e que façam a
verificação se a mesma foi feita com sucesso, simulando seu método
de callback de sucesso ou simulando uma requisição e um possível
erro no server side execute o seu método de callback de erro.
Para os exemplos de testes de Ajax usaremos o Screw.Unit como biblioteca de testes e Smoke como biblioteca de Mock. O Screw.Unit
e o Smoke são duas bibliotecas ativas e existem inúmeros forks dos
projetos presentes no Github. Utilizaremos como exemplo os fork
de rsutphin e drogus, respectivamente. Nosso primeiro exemplo
(Listagem 5) mostrará a criação de uma requisição Ajax, que simula
o retorno de um código HTTP 200, para indicar sucesso e também
o retorno de um HTML com uma mensagem de sucesso dentro de
uma tag <p>.
48
wait(function(){
expect(resultado).to(equal, “<p>Parabens!</p>”);
expect(httpStatus).to(equal, “success”);
}, 50);
});
});
</script>
</head>
<body></body>
</html>
Um teste simples para verificar se a resposta que o server side nos
enviou está correta quando o status da requisição é sucesso. Neste
primeiro exemplo, testamos o caso de sucesso da requisição, mas
e quando essa requisição não é feita com sucesso, o que deve
acontecer?
Em nosso exemplo deve ser retornado um status 500 do HTTP
e também um HTML dentro de uma tag <p> como alguma mensagem de erro. Vamos simular este comportamento e testar se o
retorno está correto. Basta adicionarmos mais um comportamento
de teste, através da função it(). Esta nova verificação será feita na
Listagem 6.
Listagem 6. Testando o caso de erro.
it(“mockando uma requisicao ajax com erro”, function() {
var resultado, httpStatus;
Smoke.Ajax.mock(“/produto”, “<p>Oops</p>”, 500);
$.ajax( {
url : “/produto”,
success : function(data, textStatus) {
resultado = data;
httpStatus = textStatus;
},
error : function(error) {
resultado = error.responseText;
httpStatus = error.status;
}
});
wait(function() {
expect(resultado).to(equal, “<p>Oops</p>”);
expect(httpStatus).to(equal, 500);
}, 50);
});
No exemplo acima, definimos dentro do método wait, que ele
deve retornar na variável resultado um valor igual a "<p>Oops</
p>" e dentro da variável httpStatus, um código igual a 500. Apenas
simulamos o comportamento da requisição Ajax usando um mock.
O exemplo que utilizamos foi apenas para demonstrar a possibilidade que o Smoke junto ao Screw.Unit nos oferece para usar um
mock de uma requisição Ajax. Utilizando uma lógica de modificação do DOM como ficaria nosso teste?
Listagem 7. Testando com Ajax alteração de um elemento na
árvore DOM.
<script type=”text/javascript”>
function callback(data) {
$(“#contentAjax”).html(data);
}
Screw.Unit(function() {
describe(“Faz um requisicao Ajax e retorna um html como sucesso”,
function() {
it(“mockando e preenchendo div com conteudo do ajax”,
function() {
var html = “<span>Camiseta Vermelha</span>”
expect($(“#contentAjax”).html()).to(equal, “”);
Smoke.Ajax.mock(“/produto/camiseta”, html, 200);
$.ajax( {
url : “/produto/camiseta”,
success : callback
});
wait(function() {
expect($(“#contentAjax”).html())
.to(equal, html);
}, 50);
});
});
});
</script>
Na Listagem 7 simulamos uma requisição que quando efetuada com
sucesso deve nos retornar um HTML que vai ser tratado por uma função criada por nos chamada callback que recebe como argumento data
que nada mais é que a resposta da requisição em texto. No exemplo acima verificamos se uma parte da árvore DOM dos nossos componentes
foram alterados de acordo com o que esperávamos. Ainda utilizando o
exemplo da Listagem 7 podemos testar de outra maneira a alteração na
árvore DOM usando apenas a função callback sem precisar simular a
requisição Ajax, como é mostrado na Listagem 8.
Listagem 8. Testando callback há alteração de um elemento
na árvore DOM.
<script type=”text/javascript”>
Screw.Unit(function() {
describe(“Preenche na div um html”, function() {
it(“testando callback de um ajax”, function() {
$(“#contentAjax”).html(“”);
var html = “<span>Camiseta Amarela</span>”;
callback(html);
expect($(“#contentAjax”).html()).to(equal, html);
});
});
});
</script>
Verificar que um elemento foi alterado e que seu Ajax está seguindo
o fluxo dos métodos de callback de sucesso e de erro é importante.
Conseguimos testar nosso código Javascript, independentemente
de chamadas ao server side. Focamos apenas no que realmente interessa, no client side da aplicação, no que é feito quando obtemos
o resultado.
Considerações finais
Como foi visto, muitas vezes não damos a devida importância aos códigos que fazemos no client side da aplicação. Valorizamos na maioria
das vezes o lado server side e podemos terminar com dados inconsistentes em nosso HTML ou mesmo efetuarmos cálculos inválidos que
podem afetar também os resultados no servidor. Testes de unidade e
abordagens como Test-Driven-Development são fáceis de serem aplicadas utilizando as ferramentas QUnit, ScrewUnit e Smoke•
Referências
• Screw Unit
http://github.com/rsutphin/screw-unit/
• Smoke
http://github.com/drogus/smoke/
• QUnit
http://docs.jquery.com/QUnit
• Blog da Caelum
http://blog.caelum.com.br/2010/07/15/arredondamento-no-java-do-double-aobigdecimal
• Blog do Luca Grulla
http://www.lucagrulla.it/blog/2010/06/15/javascript-testing/
• How to test your JavaScript code with QUnit
http://net.tutsplus.com/tutorials/javascript-ajax/how-to-test-your-javascript-codewith-qunit/
• Fault-tolerant system
http://en.wikipedia.org/wiki/Fault-tolerant_system
• Unobtrusive JavaScript
http://en.wikipedia.org/wiki/Unobtrusive_JavaScript
49
Download

JavaScript: A importância e a facilidade de criá-los