Elemar DEV

Tecnologia e desenvolvimento

Testes de unidade para Javascript dentro e fora do browser

Olá pessoal. Tudo certo?!

Javascript cresceu muito em importância nos últimos anos. No passado, era percebida como uma linguagem simplificada, para programação “simples” de páginas web. Hoje, é vista como uma linguagem poderosa, rodando tanto no cliente quanto no servidor.

Por causa do “crescimento” do Javascript, é natural pensar em alternativas para o desenvolvimento de testes de unidade automatizados.

No post de hoje, apresento alternativas para desenvolvimento e execução de testes de unidade para javascript, tanto no browser (usando o próprio QUnit), como fora dele (com um pequeno utilitário que desenvolvi).

Framework para testes de unidade

A escolha de um bom framework para testes de unidade pode simplificar muito a escrita de testes de unidade.

Há várias opções disponíveis. Entre as que conheço, tenho clara preferência pelo QUnit.

QUnit foi desenvolvido junto ao projeto jQuery. Hoje, é o framework de testes padrão para todos os projetos do grupo, incluindo o core do jQuery, jQueryUI, jQueryMobile, entre outros.

A DSL desenvolvida é bastante simples e intuitiva. Você encontra uma documentação bem completa no site oficial do projeto.

Nossos testes (cenário de exemplo)

Para ilustrar melhor nossos exemplos. Escrevi algum código e algum teste. Observe:

Comecemos pelo códigos que desejamos testar (dahn.js)

var isOdd = function(number) {
	return true;
};

var computeGreatestCommonDivisor = function() {
	return 0;
};

Como pode perceber, trata-se de uma implementação (incorreta) de um método para ver se um número é impar e outro para calcular o máximo divisor comum (MDC) para um conjunto de números.

Agora, os testes, escritos conforme DSL do QUnit.

module("Module 1");

test("isOdd", function() {
	expect(2);
	ok(isOdd(3), "passing 3, returns true.");
	ok(!isOdd(3), "passing 4, returns false.");
});


test("computeGreatestCommonDivisor", function() {
	equals(computeGreatestCommonDivisor(8, 12), 4, "passing 8 and 12, returns 4.");
});

Acho que o código é “auto-explicativo”.

Rodando os testes no browser

Já temos nossos testes, mas como executar?! Se for no browser, com QUnit, é muito simples. Observe:


<html>
<head>
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<link rel="stylesheet" href="http://code.jquery.com/qunit/git/qunit.css" type="text/css" media="screen" />
	<script type="text/javascript" src="http://code.jquery.com/qunit/git/qunit.js"></script>
</head>
<body>
	<h1 id="qunit-header">Dahn tests</h1>
	<h2 id="qunit-banner"></h2>
	<div id="qunit-testrunner-toolbar"></div>
	<h2 id="qunit-userAgent"></h2>
	<ol id="qunit-tests"></ol>

	<script type="text/javascript" src="dahn.js"></script>
	<script type="text/javascript" src="dahn_tests.js"></script>
</body>
</html>

 

Como pode ver, uso os scripts (QUnit, jQuery) e estilos (QUnit), diretamente do CDN. Se preferir, você pode baixar esses arquivos.

“Executando” o html, temos:

image

Como indicado, dois dos três testes escritos falharam.

Rodando os testes fora do browser

Já conseguimos rodar os testes no browser. Agora, vamos ver como fazer isso fora.

Para conseguir isso, escrevi um “runner” para node.js (iniciei uma série para explicar o desenvolvimento de node.js). Observe:

image

Como pode ver, solicito que o usuário forneça o nome do arquivo com o script a ser testado e o nome do arquivo com o teste.

image

Esse utilitário altera o código de saída (ERRORLEVEL) para o sistema operacional para 0, caso todos os testes falhem; e para 1, caso todos os testes passem. Assim, pode ser usado em combinação com um sistema de build.

Como foi escrito QUnit-run

QUnit é bastante extensível. É relativamente fácil expandir o framework.

Usando node.js, temos a possibilidade de executar, facilmente, código Javascript fora do browser.

Require dos módulos necessários.

var qunit = require("./qunit").QUnit,
	fs = require("fs"),
	exitcode = 0;

Perceba que estou utilizando o script comum do QUnit como módulo aqui. Para isso, fiz o download do arquivo e o coloquei na mesma pasta onde está o runner.

“atalhos” para impressão de mensagens

Como pode ver nos screenshots, utilizo:

  • vermelho para testes que falham;
  • verde para testes que passam;
  • branco (em realce) para indicar os módulos.

Para fazer isso, escrevi alguns “atalhos”. Observe

var print = console.log;

var	print_fail = function (msg) {
	print("\33[31m\33[1m" + msg + "\33[0m");
};

var	print_sucess = function (msg) {
	print("\33[32m\33[1m" + msg + "\33[0m");
};

var print_highlight = function (msg) {
	print("\n\33[1m" + msg + "\33[0m");
};

Utilizei sequências de escape ansi para gerar saídas coloridas no console.

“watch” para contabilizar o tempo total de execução.

Acho muito importante que testes “rodem” rapidamente. Por isso, acho fundamental medir o tempo total de execução dos testes.

Observe:

var stopWatch = {
	startTime: null, 
	stopTime: null,
	start: function () {
		this.startTime = new Date();
	},
	stop: function () {
		this.stopTime = new Date();
	},
	elapsedSeconds: function () {
		return (this.stopTime.getMilliseconds() - this.startTime.getMilliseconds()) / 1000;
	}
};

A lógica é bem simples. Mas, suficiente!

Extensão do QUnit

QUnit pode ser facilmente expandido. Observe minha implementação:

(function () {
	qunit.init();

	qunit.moduleStart = function (data) {
		print_highlight(data.name);
	};

	qunit.moduleDone = function (data) {
		print("\n" +
			data.failed + " failed. " + 
			data.passed + " passed. " + 
			data.total + " total."
			);
	};

	qunit.testStart = function (data) {
		print("\n  " +  data.name);
	};

	qunit.testDone = function (data) {
		print("\n  " + 
			data.failed + " failed. " + 
			data.passed + " passed. " + 
			data.total + " total."
			);
	};

	qunit.done = function (data) {
		stopWatch.stop();
		print("\nFinished in " + stopWatch.elapsedSeconds() + " seconds.");
		if (data.failed > 0) {
			exitcode = 1;
		}
	};

	qunit.begin = function() {
		stopWatch.start();
	};

	qunit.log = function (data) {
		var p = data.result ? print_sucess : print_fail,
			t = "    " ;

		p("\n██" + t + data.message );
		if (data.actual !== data.expected)
		{
			p("██" + t + "Actual = " + data.actual);
			p("██" + t + "Expected = " + data.expected);
		}
	};
} ());

Como pode ver, há funções para monitorar:

  • Início (begin) e conclusão (done) de todo o teste;
  • Início (moduleStart) e conclusão (moduleDone) de um “module”;
  • Início (testStart) e conclusão (testDone) de um “test”;
  • execução dos asserts (log)

“Disparador” do teste

A última parte da nossa pequena solução é o “disparo” propriamente dito dos testes. Observe:

if (process.argv.length < 4) {
	print_fail("Use: node qunit-run < script-file > < test-file >");
	exitcode = 2;
} else  {
	eval(fs.readFileSync(process.argv[2], "utf-8"));
	eval("with (qunit) {" + fs.readFileSync(process.argv[3], "utf-8") + "}");
	qunit.begin();
	qunit.start();
}

process.exit(exitcode);

Perceba que faço uma verificação muito simples dos argumentos; Carrego os arquivos indicados (com eval); e, no final, encerro o processo com o código de saída.

A solução é bem simples. Por isso, criei um repositório para o projeto no GitHub para que você possa me ajudar a ampliar e melhorar.

Era isso.

3 comentários em “Testes de unidade para Javascript dentro e fora do browser

  1. Cara muito bom, já estou pegando o código aqui x)
    Parabéns pelo post

  2. AbraaoAlves
    08/12/2011

    Show de bola Elemar
    Parabéns e obrigado pelo post!

  3. Pingback: Vamos aprender node.js – Parte 2 – Módulos « Elemar DEV

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

Informação

Publicado às 02/12/2011 por em Post e marcado , , , , .

Estatísticas

  • 642,411 hits
%d blogueiros gostam disto: