BT

TDD básico com Eunit

por Leandro Silva em 26 Abr 2010 |

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!.

Olá visitante

Você precisa cadastrar-se no InfoQ Brasil ou para enviar comentários. Há muitas vantagens em se cadastrar.

Obtenha o máximo da experiência do InfoQ Brasil.

Dê sua opinião

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

Receber menssagens dessa discussão

O passo 4 está errado by Adolfo Neto

O certo, em mobile_test.erl, seria:

%%
%% mobile_test.erl
%%

-module(mobile_test).

-include_lib("eunit/include/eunit.hrl").

describe_client_test_() ->
{"Mobile",
[fun should_have_a_fixed_number/0,
fun should_have_a_fixed_area_code/0,
fun should_have_a_fixed_company/0,
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()).

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

Receber menssagens dessa discussão

HTML é permitido: a,b,br,blockquote,i,li,pre,u,ul,p

Receber menssagens dessa discussão

1 Dê sua opinião

Conteúdo educacional

Feedback geral
Bugs
Publicidade
Editorial
InfoQ Brasil e todo o seu conteúdo: todos os direitos reservados. © 2006-2013 C4Media Inc.
Política de privacidade
BT