TDD é uma técnica essencial no desenvolvimento de software atual. Além de conseguir-se testes expressivos, a qualidade e design do código aumentam bastante. Na linguagem Erlang também é possível utilizar a técnica do TDD com a ferramenta Eunit. Será apresentado um guia, com alguns passos, sobre como testar uma aplicação Erlang utilizando Eunit.
Passo 1: criar a estrutura do projeto
O nome do projeto será making_tdd_with_eunit. Tem-se então a seguinte estrutura de diretórios, que é frequentemente encontrada em projetos Erlang:
- making_tdd_with_eunit |
+-- ebin+-- src
+-- test
Apesar dos diretórios terem nomes bem auto explicativos, eles tem o seguinte significado:
- ebin, conterão os binários (.beam) resultantes da compilação dos fontes;
- src, conterão os fontes (.erl) dos módulos;
- test, conterão os fontes (.erl) dos testes.
Passo 2: escrever o primeiro teste que intencionalmente falhará
No diretório test, deve ser criado o seguinte teste (mobile_test.erl):
%%
%% mobile_test.erl
%%
-module(mobile_test).
-include_lib("eunit/include/eunit.hrl").
describe_client_test_() ->
{"Mobile",
[fun should_have_a_number/0]}.should_have_a_number() ->
?assertMatch({number, _}, mobile:number()).
Feito isso, compile e rode o teste com os seguintes comandos:
$ erlc -o ebin/ test/mobile_test.erl
$ erl -pa ebin/ -noshell -run mobile_test test -run init stop
O teste falhou como mandam as regras do TDD e assim como Kent Back diz em seu livro TDD By Example:
Escreva um teste, rode todos os testes e veja-o falhar; escreva o código mais simples possível para fazê-lo passar, mesmo que num primeiro momento não seja o mais bonito e eficiente; rode todos os testes e veja-os passar; depois, refatore para remover duplicações.
Passo 3: fazer o primeiro teste passar com o código mais simples possível
No diretório src, deve ser criado o seguinte módulo (mobile.erl):
%% %% mobile.erl %% -module(mobile). -export([number/0]).number() ->
{number, "1212-1212"}.
Como feito anteriormente – no entanto, agora acrescentando o módulo mobile à compilação –, compile e rode o teste com os seguintes comandos:
$ erlc -o ebin/ src/mobile.erl test/mobile_test.erl
$ erl -pa ebin/ -noshell -run mobile_test test -run init stop
Passo 4: escrever mais testes
Agora será mostrado o ponto em que magicamente passa-se a ter testes para as quatro funções contidas no módulo mobile.
%% %% mobile_test.erl %% -module(mobile_test).-include_lib("eunit/include/eunit.hrl").
describe_client_test_() ->
{"Mobile",
[fun should_have_a_number/0,
fun should_have_a_area_code/0,
fun should_have_a_company/0,
fun should_have_a_owner/0]}.should_have_a_fixed_number() ->
?assertMatch({number, "1212-1212"}, mobile:number()).should_have_a_fixed_area_code() ->
?assertMatch({area_code, "11"}, mobile:area_code()).should_have_a_fixed_company() ->
?assertMatch({company, "DEAD"}, mobile:company()).should_have_a_fixed_owner() ->
?assertMatch({owner, "Little Jose"}, mobile:owner()).
%%
%% mobile.erl
%%
-module(mobile).-export([number/0, area_code/0, company/0, owner/0]).
number() ->
{number, "1212-1212"}.area_code() ->
{area_code, "11"}.company() ->
{company, "DEAD"}.owner() ->
{owner, "Little Jose"}.
Agora deve-se compilar e rodar os testes para ver o resultado obtido. Isso deve ser feito da seguinte maneira:
$ erlc -o ebin/ src/mobile.erl test/mobile_test.erl
$ erl -pa ebin/ -noshell -run mobile_test test -run init stop
Todos os testes estão passando e agora é o momento de fazer algumas refatorações.
Passo 5: refatorar os testes para ficarem mais fluentes
Nesse ponto, não há muito o que mudar nos testes, não há exatamente nenhuma duplicação de código e dados a ser refatorado, simplificada, nada ainda. Mas será adicionada mais fluência aos testes e visando melhorar sua comunicação, inclusive, descrevendo um cenário “mais interessante”.
%%
%% mobile_test.erl
%%
-module(mobile_test).-include_lib("eunit/include/eunit.hrl").
describe_client_test_() ->
{"Mobile",
{"when is a dummy",
[
{"should have a fixed number", fun should_have_a_fixed_number/0},
{"should have a fixed area code", fun should_have_a_fixed_area_code/0},
{"should have a fixed company", fun should_have_a_fixed_company/0},
{"should have a fixed owner", fun should_have_a_fixed_owner/0} ]}}.should_have_a_fixed_number() ->
?assertMatch({number, "1212-1212"}, mobile:number()).should_have_a_fixed_area_code() ->
?assertMatch({area_code, "11"}, mobile:area_code()).should_have_a_fixed_company() ->
?assertMatch({company, "DEAD"}, mobile:company()).should_have_a_fixed_owner() ->
?assertMatch({owner, "Little Jose"}, mobile:owner()).
Próximo passo: escrever mais testes e refatorar o módulo mobile
Até aqui, existe um módulo mobile totalmente dummy e um teste que usa apenas a macro ?assertMatch. Ainda há muito que pode ser feito, desde adicionar algum comportamento realmente útil ao módulo mobile, até melhorar os testes fazendo diferentes asserções, adicionando befor_all e after_all (como nos testes do Restfulierl, por exemplo) para estado inicial e final.
Você pode achar mais sobre Erlang e Eunit no blog CødeZøne!.