BT
x Por favor preencha a pesquisa do InfoQ !

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

Avalie esse artigo

Relevância
Estilo/Redação

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 mensagens 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 mensagens dessa discussão

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

Receber mensagens dessa discussão

1 Dê sua opinião
Feedback geral
Bugs
Publicidade
Editorial
Marketing
InfoQ Brasil e todo o seu conteúdo: todos os direitos reservados. © 2006-2016 C4Media Inc.
Política de privacidade
BT

Percebemos que você está utilizando um bloqueador de propagandas

Nós entendemos porquê utilizar um bloqueador de propagandas. No entanto, nós precisamos da sua ajuda para manter o InfoQ gratuito. O InfoQ não compartilhará seus dados com nenhum terceiro sem que você autorize. Procuramos trabalhar com anúncios de empresas e produtos que sejam relevantes para nossos leitores. Por favor, considere adicionar o InfoQ como uma exceção no seu bloqueador de propagandas.