Escolar Documentos
Profissional Documentos
Cultura Documentos
TestesAutomatizados PauloCheque Dissertacao PDF
TestesAutomatizados PauloCheque Dissertacao PDF
t
i
c
o
R
e
p
e
t
i
t
v
e
l
t
i
l
n
i
c
o
P
r
e
c
i
s
o
P
r
o
s
s
i
o
n
a
l
C
l
a
r
o
S
i
m
p
l
e
s
I
n
d
e
p
e
n
d
e
n
t
e
I
s
o
l
a
d
o
R
p
i
d
o
Obscure Test x x x x
Conditional Test Logic x x x x x x
Hard-to-Test Code x x x x x
Test Code Duplication x x x x x
Test Logic in Production x x x x
Assertion Roulette x x x x
Erratic Test x x x x x
Fragile Test x x x x x
Frequent Debugging x x x x x x x x
Manual Intervention x x x x x x
Slow Tests x x x
Buggy Tests x x x x x
Developers Not Writing Tests x x x
High Test Maintenance Cost x x x x x x x x x x x
Production Bugs x x
Tabela 5.1: Indcios de Problemas vs. Aspectos de Qualidade.
contextos, incluindo os testes automatizados [99]. Dessa forma, esses padres ajudam a criar testes de
qualidade tpicos para cenrios rotineiros em sistemas de software.
A organizao de um texto em padres o torna facilmente extensvel, ou seja, novos padres podem
ser adicionados sem a necessidade da reformulao de muitos outros trechos do trabalho. A descrio
de um padro segue uma estrutura enxuta e padronizada de tpicos pr-denidos, o que tende a facilitar
o estudo, principalmente para os leitores que j conhecem parte da teoria, pois os tpicos podem ser
lidos em uma ordem arbitrria.
Alguns dos padres que sero descritos nos prximos captulos j foram identicados por outros au-
tores e nomeados em ingls, mas devido sua grande importncia para a criao de testes automatizados
de qualidade, eles tambm foram includos nessa dissertao. Os nomes desses padres foram traduzi-
dos para o portugus, mas seus nomes originais em ingls foram mantidos para fcil identicao. Alm
disso, para esses casos, haver um tpico com as principais referncias da soluo. Quanto aos outros
padres, eles foram identicados e nomeados pelo autor desta dissertao. No entanto, independente da
autoria, todos sero descritos de acordo com a seguinte estrutura:
Nome: Nome do padro. Caso o nome possua a traduo para o ingls, signica que ele j foi identi-
cado por outros autores.
Tipo: Neste trabalho, um padro poder ser dos tipos Testabilidade, Organizacional, Robustez, Quali-
dade ou Desempenho. Testabilidade indica que o padro ajuda a melhorar essa caracterstica do
sistema em teste; Organizacional aponta que um padro utilizado para ajudar a organizar os
casos de teste; j os padres de Robustez servem para tornar os testes menos frgeis; os de Quali-
dade so teis para fazer anlises interessantes do sistema em teste em busca de erros; por ltimo,
Desempenho para aumentar a velocidade de execuo das baterias de testes.
Quando utilizar: Contm sugestes de situaes tpicas que o padro deve ser utilizado.
55
Inteno: Contm uma breve descrio dos objetivos do padro.
Motivao: Apresenta os problemas que o padro ajuda a resolver.
Soluo: Contm a descrio da soluo proposta pelo padro.
Consequncias: Discute como o padro ajuda a solucionar os problemas descritos no tpico anterior.
Implementao: Contm informaes de como o padro pode ser implementado. Se a implementao
for especca para um nicho de sistemas, o nome do tpico ir indic-lo, por exemplo, Implemen-
tao - WUI. Um mesmo padro poder ter tpicos de implementao para mais de um nicho de
sistemas.
Exemplo: Contm exemplos de cdigo-fonte demonstrando o padro. Os exemplos neste trabalho
utilizaro as linguagens C, Java, Scala ou Python. O nome do tpico ir informar a linguagem e as
tecnologias utilizadas, por exemplo, Exemplo - Python/UnitTest/QAssertions. Um mesmo padro
poder ter exemplos em mais de uma linguagem e as referncias das tecnologias esto nas pginas
iniciais da dissertao.
Padres Relacionados: Outros padres que possuem algum relacionamento importante com o padro
correspondente.
Usos Conhecidos: Sistemas ou ferramentas que utilizaram a soluo proposta e que serviram de
amostra para nomeao do padro. As referncias esto nas pginas iniciais da dissertao.
Referncias: Tpico no obrigatrio. Contm outras referncias para o padro, caso ele j tenha sido
nomeado por outro autor.
5.4 Denio de Antipadro
Antipadres tambm descrevem solues para problemas recorrentes, mas so propostas equivocadas,
pois causam problemas para o desenvolvimento dos sistemas e dos testes. Contudo, a descrio dos
antipadres ser mais simples do que dos padres.
Os antipadres, com raras excees, nunca devem ser utilizados. Em relao Implementao, os
programadores no precisam conhecer todos os detalhes das solues. Quanto aos Usos Conhecidos,
no seria elegante expor negativamente projetos sem uma boa causa. J a Inteno e a Motivao foram
agrupados em um nico item, Contexto, para simplicar e substituir a conotao positiva dos termos.
Os tpicos Consequncias e Padres Relacionados foram substitudos pelo tpico Indcios de Problemas
Relacionados. Sendo assim, a organizao de um antipadro segue a estrutura abaixo:
Nome: Nome do antipadro. Se o nome tiver a traduo para o ingls, signica que ele j foi identi-
cado por outros autores como antipadro ou at mesmo como padro. Em alguns dos casos, o
autor desse trabalho interpretou padres nomeados por outros trabalhos como solues ruins e,
portanto, devem ser entendidas como antipadres.
Tipo: Os antipadres podero ser dos tipos Organizacional, Robustez ou Testabilidade. A explicao
de cada um dos tipos anloga descrita na seo anterior, com a diferena de que as solues
indicadas so prejudiciais.
Contexto: Apresenta, de forma resumida, as situaes tpicas onde aparecem esses antipadres.
Exemplo: Similar ao tpico de um padro.
56
Indcios de Problemas Relacionados: Mostra as ligaes dos indcios de problemas (Seo 5.2) que
podem ser causados pelo antipadro, ou seja, as possveis consequncias causadas pela soluo
equivocada.
Referncias: Similar ao tpico de um padro.
57
58
Captulo 6
Testes de Unidade
Como foi descrito na Seo 3.3.1, um teste de unidade verica a correo dos menores trechos de
cdigo que possuem um comportamento denido e nomeado. Normalmente associamos uma unidade
a um mtodo ou funo do sistema, mas em certas situaes podemos entender unidade como blocos,
classes que geralmente so muito simples, mdulos ou at mesmo constantes (variveis com valores
xos) [99].
Os testes de unidade so, via de regra, os mais importantes dentre os demais tipos de teste, pois
so os mais apropriados para vericao da correo, que a premissa bsica de qualquer sistema de
software de qualidade, alm de ser pr-requisito dos outros aspectos de qualidade. Apesar de existirem
erros de correo insignicantes, que no impedem o bom funcionamento do sistema ou que trazem
pouco prejuzo se comparado com outras irregularidades, falhas na correo do sistema so, geralmente,
crticas e mais intolerveis do que problemas de desempenho, segurana ou usabilidade [70].
Independentemente da gravidade dos erros de correo, sempre desejvel que o sistema esteja
completamente correto, isto porque falhas na correo do sistema podem ocultar outros defeitos do soft-
ware e at mesmo desvalorizar os resultados de outras baterias de testes. Por exemplo, um algoritmo
que no trata todas as situaes esperadas pode ser muito mais rpido do que a verso correta do algo-
ritmo, sendo assim, os testes de desempenho realizados sobre o sistema trazem resultados que podem
ser descartados. Por isso, recomendado que outras baterias de testes s sejam executadas depois que
as baterias de testes de correo estejam completas e sem falhas.
De qualquer maneira, todos os tipos de testes e ferramentas podem ajudar a melhorar a correo dos
sistemas, principalmente em relao a situaes raras e pequenos detalhes que passam despercebidos
durante a implementao. Alm disso, outros tipos de testes submetem o sistema a situaes peculiares
que podem exercitar mdulos e dependncias que no foram previamente testados. Contudo, impor-
tante no atribuir a responsabilidade de encontrar erros de correo a baterias de outros tipos de testes,
principalmente por causa do uso de ferramentas que no so apropriadas e, portanto, induzem ocorrn-
cia de diversos antipadres que tornam os testes difceis de escrever e manter. As ferramentas para testes
de unidade so geralmente bibliotecas de cdigo-fonte que disponibilizam funcionalidades para facilitar
o manuseio e vericao de trechos do sistema, e essas ferramentas j so sucientes para a automao
produtiva dos testes de unidade durante o desenvolvimento.
Ao contrrio de outros tipos de testes que precisam que determinados mdulos do sistema estejam
nalizados para serem executados com xito, os testes de unidade devem ser escritos e executados sobre
pequenas pores de cdigo que no constituem isoladamente uma funcionalidade do sistema do ponto
de vista dos usurios nais. Essa caracterstica fundamental para encontrar erros de correo nos
estgios iniciais de desenvolvimento e para evitar desperdcio de tempo com depurao, pois os testes
de unidade podem ser bem especializados e a falhas nos testes tendem a ser facilmente localizadas [68].
59
6.1 Arcabouos para Testes de Unidade
Executar e testar um sistema so tarefas intrnsecas ao desenvolvimento de sistemas de software. Du-
rante a implementao comum executar o sistema periodicamente, seja para averiguar seu progresso,
encontrar erros ou at mesmo para incentivo pessoal pela visualizao do trabalho que est sendo re-
alizado. No entanto, s possvel executar certas funcionalidades do sistema quando um conjunto de-
terminado de mdulos est pelo menos parcialmente implementado. Por exemplo, pode ser necessrio
que a interface de usurio, assim como o ponto de partida da execuo do sistema (mtodo main), que
obrigatrio para muitas linguagens de programao, estejam implementados para que o usurio possa
fazer a chamada de uma determinada funcionalidade.
Uma soluo trivial para executar pequenos trechos do sistema sem depender de outros mdulos a
criao de mtodos main (geralmente implementados no prprio arquivo de cdigo da funcionalidade)
exclusivos para a execuo do trecho de cdigo desejado juntamente com a utilizao de comandos
print, que so utilizados para imprimir valores de variveis. Esta soluo nada mais que um teste de
unidade manual, ou seja, executado um pequeno trecho do sistema e as vericaes so feitas por um
ser humano que compara o valor impresso com o valor esperado.
Alm de todos os problemas da abordagem manual que j foram discutidos na Parte I, essa soluo
mistura o cdigo dos testes com o do sistema, prejudicando a legibilidade, depurao e manuteno
do cdigo, que pode implicar piora da correo do sistema a mdio e longo prazo. Desses padres e
antipadres de testes manuais de unidade surgiram os arcabouos que deram origem automao de
testes [20].
Por volta de 1994, Kent Beck criou o arcabouo SUnit [13] para fazer testes automatizados em seus
projetos que utilizavam a linguagem SmallTalk. At hoje o SUnit utilizado, alm de servir como
referncia para implementao de arcabouos semelhantes para outras linguagens de programao. Em
1998, por exemplo, Kent Beck e Erich Gamma desenvolveram o arcabouo JUnit para a linguagem Java
com base no SUnit. Kent Beck e colaboradores tambm escreveram na pgina Web ocial do JUnit um
artigo descrevendo passo a passo a sua implementao para referncia
1
.
O conjunto de arcabouos que so inspirados na arquitetura do SUnit so conhecidos como famlia
xUnit. Entre eles esto o PyUnit para Python, CppUnit para C++, JsUnit para JavaScript etc. Todos eles
facilitam a escrita do cdigo dos testes evitando replicao de trabalho e sem prejudicar o cdigo do
sistema principal.
Cada arcabouo da famlia xUnit possui suas particularidades, devido, principalmente, s diferenas
das linguagens, mas todos seguem uma mesma estrutura bsica e extensvel que facilita a escrita e acom-
panhamento dos testes automatizados. Esses arcabouos possuem basicamente trs responsabilidades:
1) possibilitar a criao e organizao de casos de testes com pouco esforo e sem replicao de cdigo;
2) facilitar a comparao dos valores obtidos com os valores esperados e 3) gerar um relatrio claro dos
resultados obtidos.
Um caso de teste representado por um mtodo quando so utilizadas linguagens orientadas a ob-
jetos, ou por uma funo no caso de linguagens estruturadas. Contudo, nem todo mtodo ou funo
do cdigo-fonte um caso de teste, por isso as ferramentas utilizam metadados
2
(Figura 6.2) ou con-
venes
3
(Figura 6.1) para identicar quais deles devem ser interpretados como casos de teste. Quando a
ferramenta xUnit executada, ela processa apenas os mtodos/funes que so denidos como casos de
testes, o que dispensa escrever mtodos main e tambm permite rodar todos casos de teste com apenas
um comando.
Para comparar os valores obtidos com os valores esperados automaticamente, basta substituir os
1
http://junit.sourceforge.net/doc/cookstour/cookstour.htm
2
A verso 4 ou superior do JUnit associa mtodos a casos de teste atravs da anotao Java @Test.
3
Muitas ferramentas seguem a conveno de que se o nome do mtodo comea com a palavra test, ento ele um caso de
teste.
60
1 // Referncias do JUnit
2 import junit.framework.TestCase;
3
4 // Aps a execuo desta classe de teste, ser impresso no console:
5 // Ser invocado pelo JUnit
6 public class JUnit35Exemplo extends TestCase {
7
8 // Conveno: mtodo comea com a palavra "test"
9 public void testUmMetodoDeTeste() {
10 System.out.println("Ser invocado pelo JUnit");
11 }
12
13 public void umMetodoAuxiliar() {
14 System.out.println("No ser invocado pelo JUnit");
15 }
16 }
Figura 6.1: Denindo mtodos de teste com JUnit 3.5.
1 // Referncias do JUnit
2 import org.junit.Test;
3
4 // Aps a execuo desta classe de teste, ser impresso no console:
5 // Ser invocado pelo JUnit
6 public class JUnit4Exemplo {
7
8 @Test // Metadado informando que este um mtodo de teste
9 public void umMetodoDeTeste() {
10 System.out.println("Ser invocado pelo JUnit");
11 }
12
13 public void umMetodoAuxiliar() {
14 System.out.println("No ser invocado pelo JUnit");
15 }
16
17 }
Figura 6.2: Denindo mtodos de teste com JUnit 4 ou superior.
61
comandos print da abordagem manual por comparaes (comandos if). Contudo, os arcabouos
disponibilizam funcionalidades que fazem as vericaes (asseres), substituindo esses comandos com
as vantagens de minimizar a replicao de cdigo de comparao e tambm de armazenar as infor-
maes pertinentes do caso de teste, como o resultado e a causa dos erros. A Figura 6.3 apresenta
exemplos de vericaes comuns utilizando os comandos bsicos de vericao do JUnit e, tambm, a
biblioteca Hamcrest, que torna o cdigo dos testes mais prximo da linguagem natural se ignorarmos os
parenteses, vrgulas e pontos da linguagem Java. Essa biblioteca tem como base o mtodo de vericao
assertThat e Matchers que so objetos que fazem as comparaes de forma conveniente.
Na Figura 6.4 h um exemplo de um teste escrito em Java com JUnit e Hamcrest. J na Figura 3.1
da Parte I podemos ver um exemplo de testes em Python de uma funo que verica se um nmero
primo.
Todas as informaes armazenadas so expostas no relatrio nal da bateria de testes, que so funda-
mentais para identicar os erros e diminuir o tempo perdido com depurao. Os relatrios tambm so
teis para o acompanhamento da automao dos testes e para documentao do sistema. Existem diver-
sas ferramentas que armazenam o histrico de resultados dos testes para gerar grcos e representaes
que ajudam a gerenciar os projetos.
6.1.1 Set up e Tear down
Muitos cenrios de testes (de qualquer tipo) s podem ser realizados segundo conguraes especcas
do ambiente, de dados e de estados de objetos. Por isso, comum a prtica de preparar um ambiente
propcio para um nico ou para um conjunto de casos de testes, sejam eles automatizados ou manuais.
Tendo o ambiente congurado, um caso de teste o manipula indiscriminadamente de acordo com
seus objetivos e faz as vericaes necessrias. Aps o trmino do teste, o ambiente alterado no
mais necessrio, portanto, uma boa prtica da automao de testes descart-lo, seja simplesmente para
liberar memria ou at mesmo para facilitar a criao do ambiente dos prximos cenrios de testes.
Por isso, outra caracterstica comum aos arcabouos da famlia xUnit a chamada implcita a mto-
dos prprios para prepararem e destruirem os ambientes dos testes, com os objetivos de padronizar, sim-
plicar e reduzir o cdigo-fonte dos testes. Como o prprio arcabouo faz a chamada desses mtodos, o
desenvolvedor no precisa fazer as chamadas, apenas saber em que momento eles sero chamados.
Da mesma forma que os mtodos de testes, os mtodos de preparao e destruio do ambiente
so denidos atravs de convenes ou metadados. Geralmente, o mtodo de criao do ambiente
identicado pelo nome set up, enquanto os de limpeza do ambiente de tear down. Quando so
utilizados metadados, possvel dar nomes aos mtodos mais coerentes com o que est sendo realizado.
No obstante, muitos arcabouos fornecem chamadas implcitas para esses mtodos para diversos
escopos: escopo de caso de teste, onde a chamada implcita realizada antes e depois de cada caso de
teste; escopo de grupos de testes, os quais as chamadas so feitas apenas antes e depois da execuo
de todos os casos de testes de um determinado conjunto; e, tambm, escopo da bateria de testes, onde
apenas uma chamada feita antes da execuo da bateria inteira de todos os testes e uma aps o trmino
da execuo dos mesmos.
O arcabouo TestNG, para Java, bastante exvel em relao aos escopos de set up e tear down.
A Figura 6.5 apresenta um esqueleto de teste para demonstrar o uxo das chamadas implcitas do ar-
cabouo.
Como os testes de unidade so isolados, muito comum o uso dos mtodos de set up apenas para
a inicializao dos objetos necessrios. Essa responsabilidade mesma de um construtor de um objeto,
entretanto, deve ser evitado sobrescrever os construtores das classes de testes para evitar interferncia
no uxo de controle dos arcabouos.
J os mtodos de tear down so utilizados principalmente para destruir os objetos, dados e liberar
a memria. No caso das linguagens que possuem coletor de lixo automtico, os mtodos de tear down
62
1 // Referncias Java
2 import java.util.*;
3 // Referncias do JUnit
4 import org.junit.Test;
5 import static org.junit.Assert.*;
6 // Referncias do Hamcrest
7 import static org.hamcrest.Matchers.*;
8
9 // Todos as verificaes dos testes a seguir so vlidas
10 public class ExemploDeVerificacoesTest {
11 @Test public void comparandoInstanciaDosObjetos() {
12 Object o1 = new Object();
13 Object o2 = o1;
14 assertSame(o1, o2);
15 assertThat(o1, is(sameInstance(o2)));
16 o2 = new Object();
17 assertNotSame(o1, o2);
18 assertThat(o1, is(not(sameInstance(o2))));
19 }
20
21 @Test public void comparandoStrings() {
22 assertTrue("a".equalsIgnoreCase("A"));
23 assertThat("a", is(equalToIgnoringCase("A")));
24
25 assertTrue("..zzz!!".contains("zzz"));
26 assertThat("..zzz!!", containsString("zzz"));
27
28 assertTrue("..zzz!!".startsWith(".."));
29 assertThat("..zzz!!", startsWith(".."));
30 }
31
32 @Test public void comparandoNumeros() {
33 assertEquals(10, 10);
34 assertThat(10, is(equalTo(10)));
35
36 assertTrue(11 >= 11);
37 assertThat(11, is(greaterThanOrEqualTo(11)));
38
39 assertTrue(10 < 11);
40 assertThat(10, is(lessThan(11)));
41 }
42
43 @Test public void comparandoPontosFlutuantes() {
44 assertEquals(10.493, 10.5, 0.1); // Preciso: 1 dcimo de diferena
45 assertThat(10.493, is(closeTo(10.5, 0.1)));
46 }
47
48 @Test public void comparandoListas() {
49 List<Integer> list = Arrays.asList(1, 2, 3, 4);
50 assertTrue(list.contains(1));
51 assertThat(list, hasItem(1));
52 }
53 }
Figura 6.3: Exemplos de vericaes com JUnit e Hamcrest.
63
1 // Referncias do JUnit
2 import org.junit.Test;
3 import static org.junit.Assert.*;
4 // Referncias do Hamcrest
5 import static org.hamcrest.Matchers.*;
6
7 public class MathTests {
8 final static double PRECISAO = 0.01;
9
10 @Test
11 public void testaValoresMuitoConhecidosDaFuncaoCosseno() {
12 assertThat(Math.cos(0), is(closeTo(1.0, PRECISAO)));
13 assertThat(Math.cos(90), is(closeTo(0.0, PRECISAO)));
14 assertThat(Math.cos(180), is(closeTo(-1.0, PRECISAO)));
15 assertThat(Math.cos(270), is(closeTo(0.0, PRECISAO)));
16 assertThat(Math.cos(360), is(closeTo(1.0, PRECISAO)));
17 }
18
19 @Test
20 public void testaValoresDeAngulosComunsDaFuncaoCosseno() {
21 assertThat(Math.cos(30), is(closeTo(Math.sqrt(2)/2, PRECISAO)));
22 assertThat(Math.cos(45), is(closeTo(0.5, PRECISAO)));
23 assertThat(Math.cos(60), is(closeTo(Math.sqrt(2)/2, PRECISAO)));
24 }
25
26 }
Figura 6.4: Exemplo de Teste em Java com JUnit e Hamcrest.
so dispensveis nos testes de unidade, pois aps a realizao de todos os testes de uma classe, todas as
variveis de instncia sero coletadas. Nesse caso, o uso dessa funcionalidade uma micro-otimizao
desnecessria, pois o aumento da complexidade do cdigo dos testes por causa do cdigo adicional no
recompensada por milissegundos de velocidade.
Um caso excepcional quando um nico mtodo de teste consome muita memria, ento pode ser
interessante liber-la antes de realizar os outros casos de testes da mesma classe. Ainda, caso sejam
utilizados variveis globais, pode haver a preocupao de vazamento de memria, onde o tear down
tambm ser til. A Figura 6.6 mostra um exemplo de teste de unidade em Java onde interessante
preparar e destruir o ambiente de teste.
Entretanto, os mtodos de set up e tear down so mais importantes para testes de integrao. Os
testes com persistncia de dados e de interface de usurio podem depender de ambientes complexos
e propcios para tornarem os testes frgeis. Por exemplo, comum popular um banco de dados para
realizao de um teste e remover os dados adicionados aps a concluso do mesmo. Assim, dados
criados por um teste no afetam os outros. J para interfaces de usurio, a preparao e destruio do
ambiente refere-se comumente abertura e fechamento dos navegadores, pginas e janelas.
6.2 Objetos Dubls (Test Doubles)
Algumas vezes difcil testar um sistema porque ele pode depender de componentes que so difceis
de serem utilizados em um ambiente de teste [93, 138]. Tais componentes podem ser bancos de dados,
sistemas de arquivos, redes, servios Web, bibliotecas e at mesmo do relgio do computador, no caso
de funcionalidades que envolvem datas e instantes.
Para essas situaes, mais produtivo vericar a correo do sistema atravs de testes de unidade
64
1 // Referncias do TestNG
2 import org.testng.annotations.*;
3
4 // Aps a execuo desta classe de teste, ser impresso no console:
5 // @BeforeSuite => @BeforeTest => @BeforeClass
6 // @BeforeMethod => @Test: teste 1 => @AfterMethod
7 // @BeforeMethod => @Test: teste 2 => @AfterMethod
8 // @AfterClass => @AfterTest => @AfterSuite
9 public class TestNGExemplo {
10 /* Mtodos de set up */
11 @BeforeSuite public void antesDeTodasAsClassesDeTestes() {
12 System.out.print("@BeforeSuite => ");
13 }
14
15 // Ao contrrio do BeforeClass, BeforeTest roda mesmo que no tenha teste
16 @BeforeTest public void antesDessaClasseDeTestes() {
17 System.out.print("@BeforeTest => ");
18 }
19
20 @BeforeClass public void antesDoPrimeiroMetodoDeTesteDessaClasse() {
21 System.out.println("@BeforeClass");
22 }
23
24 @BeforeMethod public void antesDeCadaMetodoDeTesteDessaClasse() {
25 System.out.print("@BeforeMethod => ");
26 }
27
28 /* Mtodos de tear down */
29 @AfterMethod public void depoisDeCadaMetodoDeTesteDessaClasse() {
30 System.out.println("@AfterMethod");
31 }
32
33 @AfterClass public void depoisDoPrimeiroMetodoDeTesteDessaClasse() {
34 System.out.print("@AfterClass => ");
35 }
36
37 @AfterTest public void depoisDessaClasseDeTestes() {
38 System.out.print("@AfterTest => ");
39 }
40
41 @AfterSuite public void depoisDeTodasAsClassesDeTestes() {
42 System.out.print("@AfterSuite");
43 }
44
45 /* Mtodos de teste */
46 @Test public void metodoDeTeste1() {
47 System.out.print("@Test: teste 1 => ");
48 }
49
50 @Test public void metodoDeTeste2() {
51 System.out.print("@Test: teste 2 => ");
52 }
53 }
Figura 6.5: Mtodos de set up e tear down do arcabouo TestNG para Java.
65
1 // Referncias do TestNG
2 import org.testng.annotations.*;
3 // Referncias do sistema em teste ocultas
4
5 public class PilhaComTamanhoLimitadoTests {
6 Pilha pilha; // varivel utilizada em todos os testes
7
8 /* Set up */
9 @Before public void inicializaObjetos() {
10 pilha = new Pilha();
11 }
12
13 /* Tear down */
14 @After public void liberaMemoria() {
15 pilha.esvazia();
16 pilha = null;
17 System.gc(); // Agilizando a execuo do Garbage Collector
18 }
19
20 // Teste que consome bastante memria
21 @Test public void pilhaNaoAceitaMaisElementosDoQueLimiteEstipulado() {
22 pilha.setAlturaMaxima(1000);
23 for(int i = 0; i < 1000; i++)
24 pilha.coloca(i);
25 assertEquals(1000, pilha.altura());
26 pilha.coloca(i);
27 assertEquals(1000, pilha.altura());
28 }
29 }
Figura 6.6: Exemplo tpico de uso dos mtodos set up e tear down.
66
em vez de testes de integrao. Primeiramente, os testes de unidade solucionam os problemas de baixa
testabilidade do sistema em teste. Alm disso, todas as outras caractersticas de qualidade, descritas na
Seo 5.1, so mais facilmente asseguradas quando um cenrio de teste isolado, ou seja, ele tende a
car mais rpido, independente e repetitvel.
O que caracteriza um teste de unidade justamente o isolamento de um trecho de cdigo do resto
do sistema e do ambiente. Isolar um trecho de cdigo signica substituir todas as suas dependncias,
que podem ter implementaes lentas, incompletas ou que prejudicam a testabilidade, por dependncias
controladas. Dessa maneira, o trecho de cdigo em teste trabalha sob situaes ideais, supondo que todas
suas dependncias esto corretas. Inclusive, essa caracterstica ajuda a dispensar a prtica de depurao,
pois, se algum teste falhar, ca explcito o trecho de cdigo que est o problema.
No entanto, isolar um trecho de cdigo pode ser uma tarefa complicada. A diculdade deve-se
principalmente testabilidade do sistema (Seo 10.3). Quanto mais entrelaado estiverem os mdulos
em teste, mais difcil ser para substituir as dependncias por objetos controlados [113].
Dado que um mdulo sucientemente coeso para isolar seu comportamento, possvel que isso
seja feito comumente de duas maneiras: a primeira inserir, errneamente, lgica de teste no cdigo de
produo, que um indcio de problema (Seo 5.2); a segunda, mais elegante, fazer com que os testes
substituam, apenas dentro do seu contexto e durante sua execuo, as dependncias da funcionalidade
em teste por mdulos e objetos que apenas respondam o que o cenrio de teste espera para poder fazer
suas vericaes.
No caso de linguagens orientada a objetos, os testes podem substituir os objetos dependentes por
objetos equivalentes, mas que possuem o comportamento mnimo desejado para realizao do teste.
Por exemplo, atravs de herana, pode-se criar subclasses que sobrescrevem a implementao original
por uma simplicada. Nas linguagens em que at os mtodos so objetos, possvel, simplesmente,
substitu-los por implementaes temporrias durante a execuo do cenrio de teste. Ainda, existem
bibliotecas, tais como Mockito, EasyMock e JMock para Java, que criam objetos que seguem o compor-
tamento desejado, sem a necessidade de implement-los.
Esses objetos que so criados no escopo dos testes para substituir dependncias so chamados gener-
icamente de Objetos Dubls, em analogia aos dubls de cinema [99]. Contudo, h diversos tipos de
Objetos Dubls (Figura 6.7), que so apropriados para situaes especcas. Nas sees 6.4.3 at 6.4.7
h a descrio detalhada dos cinco tipos de Objetos Dubls j descritos por Meszaros: Objeto Tolo,
Stub, Falsicado, Emulado e Espio, respectivamente. Na Seo 6.4.8, descrito um novo tipo que foi
identicado pelo autor, o Objeto Prottipo.
A escolha do tipo de Objeto Dubl a ser utilizado depende prioritariamente do que se est querendo
vericar, pois nem todos eles podem ser utilizados dentro de um contexto. Por exemplo, somente os Ob-
jetos Falsicados e os Espies so capazes de imitar um algoritmo, ou seja, de fornecer dados dinmicos
para a funcionalidade em teste.
A Tabela 6.1 faz uma comparao dos objetos dubls de acordo com quatro caractersticas impor-
tantes: (1) se o dubl exercitado pelo teste, ou seja, se ele inuencia diretamente no resultado do teste;
(2) se o dubl fornece informaes enganosas que inuenciam no resultado gerado pela funcionalidade
em teste, sejam elas dados estticos ou gerados dinamicamente por algoritmos simplicados; (3) a ca-
pacidade do dubl de armazenar informaes sobre o que foi executado, o que permite fazer vericaoes
na forma que um algoritmo executado e (4) caso o objeto dubl precise ou no seguir uma interface
denida, o que pode ser importante para realizar testes de algoritmos reexivos ou com programao a
aspectos.
Depois que se sabe quais padres so viveis de serem utilizados, a escolha deve-se basear na sim-
plicidade, ou seja, qual deles torna a implementao do teste mais enxuta e legvel. Por exemplo, Se o
objeto servir apenas para evitar erros de compilao ou erros indiretos de execuo, ou seja, ele no ser
exercitado diretamente pelo teste, ento Objeto Tolo o que deve ser escolhido porque o mais fcil de
implementar. No entanto, no existe uma regra para isso, pois varia de acordo com as particularidades
67
de cada contexto e das ferramentas disponveis de objetos dubls. Mais detalhes e exemplos so podem
ser encontrados na Seo 6.4.
Figura 6.7: Tipos de Objetos Dubls.
h
h
h
h
h
h
h
h
hh
Objeto Dubl
Caractersticas
Exercitado pelo Teste Fornecimento de Dados para o SUT Armazena Informaes da Execuo Interface Predenida
Objeto Tolo (Dummy Object) No No Fornece No Sim
Objeto Stub (Test Stub) Sim Esttico ou Dinmico No Sim
Objeto Falsicado (Fake Object) Sim Esttico ou Dinmico No Sim
Objeto Emulado (Mock Object) Sim Esttico Sim Sim
Objeto Espio (Spy Object) Sim Esttico ou Dinmico Sim Sim
Objeto Prottipo Sim Esttico No No
Tabela 6.1: Comparao de algumas caractersticas dos Objetos Dubls.
6.3 Boas Prticas de Automao
A automao de testes pode ter diversos problemas de cdigo-fonte, comportamento e projeto, como
foi discutidos na Seo 5.2. Entretanto, muitos desses problemas podem ser facilmente evitados atravs
da utilizao de boas prticas de automao de testes. Algumas das boas prticas mais gerais, teis
para quaisquer tipos de teste, j foram discutidas no Captulo 4. Agora, sero abordadas boas prticas
direcionadas para testes de unidade.
6.3.1 Cdigo-Fonte
Apesar de que o cdigo-fonte dos testes precisa receber a mesma ateno dedicada ao cdigo-fonte do
sistema em teste (Seo 5.1), no necessrio seguir, rigorosamente, todas as boas prticas de progra-
mao conhecidas pela equipe de desenvolvimento. Por exemplo, algumas prticas de otimizaes ou
de modularizao podem trazer muitos benefcios para o sistema, tornando-o mais rpido e exvel, mas
elas podem prejudicar outras caractersticas, como a clareza e simplicidade.
Para os testes, o ideal que seu cdigo seja o mais simples, enxuto e legvel possvel. Deve-se
encontrar o equilbrio entre o uso de todo o poder das linguagens de programao com a clareza e
simplicidade de um texto em linguagem natural. Para encontrar esse equilbrio, algumas boas prticas
podem ajudar, como sero descritas abaixo.
Sem Rigores das Linguagens de Programao: Muitas linguagens permitem atribuir muitas pro-
priedades a uma varivel, mtodo ou classe. Por exemplo, em Java, possvel denir uma varivel
como pblica (public), privada (private), protegida (protected), de classe (static), constante (nal)
etc. Todas essas propriedades podem ser muito importantes para a arquitetura do sistema, mas,
para os testes, elas so dispensveis, pois elas apenas poluem o cdigo-fonte com complexidade
desnecessria. O design do cdigo-fonte dos testes deve ser to simples a ponto de no necessitar
desses rigores de arquitetura. Os casos excepcionais so quando as ferramentas utilizadas neces-
sitarem de alguma propriedade especca. Por exemplo, o JUnit requer que todos os mtodos de
teste sejam pblicos.
68
Ainda, as linguagens podem oferecer diferentes convenes de cdigo-fonte, sendo que algumas
so mais rgidas do que as outras. Por exemplo, em C o padro ANSI mais rgido do que o
POSIX. No caso de Java, possvel congurar ferramentas auxiliares e IDEs para denir quais
so as convenes que devem ser seguidas. Para os testes, deve-se sempre optar pelas convenes
menos controladoras, que possibilitem a criao de cdigo-fonte menos rigoroso.
Nomes Auto-Explicativos: Os nomes das variveis, mtodos e classes podem seguir convenes, desde
que eles sejam claros e auto-explicativos. Deve-se dar preferncia a nomes completos e longos do
que siglas e abreviaes que tornem o signicado no-bvio.
Linguagem Ubqua da Equipe: Alm dos nomes deverem ser auto-explicativos, prefervel que eles
utilizem uma linguagem ubqua entre clientes, programadores, testadores etc. Ainda, deve ser
evitado o uso de sinnimos para diminuir o vocabulrio utilizado pelo projeto.
Sem Variveis Globais Mutveis: Variveis globais, compartilhadas e de classe devem ser evitadas
tanto no sistema como nos testes, mas h situaes em que elas trazem muitos benefcios para as
aplicaes. No entanto, para os testes, elas devem sempre ser evitadas, pois elas no s aumentam
a complexidade dos testes, como tambm favorecem criao de casos de testes dependentes, o
que os tornam mais difceis de serem paralelizados.
Arquitetura Simples: Por mais que um design de arquitetura possa tornar o cdigo-fonte mais exvel
e diminuir a replicao de cdigo, ele tambm pode tornar o cdigo-fonte mais difcil de ser
entendido. Flexibilidade e no-replicao de cdigo tambm so importantes para os testes, mas
deve haver um equilbrio com a simplicidade. Boas tcnicas de orientao a objetos, design e
arquitetura tambm devem ser utilizadas ao escrever os cenrios de testes. Contudo, devido
prpria simplicidade do cdigo dos testes automatizados e do uso de arcabouos, no deve ser
grande o esforo para criar baterias organizadas de testes automatizados. De maneira geral, o
design das classes de testes no deve ser muito mais complexo do que simples relacionamentos
de herana e colaborao.
Sem Otimizaes Desnecessrias: Se at para os sistemas as otimizaes so recomendadas apenas
para as funcionalidades com os gargalos de desempenho, para os testes essa recomendao
ainda mais enftica. De maneira geral, otimizao dos testes deve ser feita apenas por ferramentas
auxiliares e de maneira transparente, ou seja, sem a necessidade de alterar o cdigo-fonte dos
testes. Na prtica, essas ferramentas podem executar os testes paralelamente ou com alguma
estratgia mais elaborada.
6.3.2 Refatoraes Comuns
Como discutido na seo anterior, o cdigo dos testes deve ser organizado, claro, legvel e sem repli-
cao. Quando a implementao no atende a esses requisitos, ainda possvel melhor-la atravs de
refatorao, que deve ser uma tarefa rotineira da automao dos testes.
Existem diversos estudos sobre refatorao de cdigo de teste com o intuito de melhorar a quali-
dade de testes j implementados [53, 143]. Entretanto, mesmo durante a criao de novos cenrios de
testes comum a realizao de refatoraes, principalmente para facilitar a adio de novos cenrios
de vericao. A seguir h breves comentrios das refatoraes que so mais frequentemente utilizadas
durante a automao de testes. Por causa da modelagem simples do cdigo dos testes, as refatoraes
mais usadas esto entre as mais simples [59, 122].
Extrair Mtodo: Muitos mtodos de testes so parecidos, ento comum extrao de fragmentos para
mtodos auxiliares ou at mesmo para os mtodos de set up e tear down.
69
Extrair Classe: Quando uma classe de teste comea a car muito extensa, com muitos mtodos auxil-
iares e de teste, pode ser um sinal que ela precisa ser dividida, ou, at mesmo, que as classes do
sistema que esto sendo testadas possuem muitas responsabilidades. O mesmo raciocnio pode
ser empregado quando os mtodos de set up e tear down esto muito extensos e complexos.
Extrair Superclasse: Quando diversas classes de testes possuem mtodos de set up ou tear down semel-
hantes, pode ser um indcio de que uma classe base para os testes pode ser criada para evitar
replicao de cdigo.
Renomear Classes, Mtodos e Variveis: As refatoraes anteriores de extrao sempre produzem no-
vas variveis e classes, logo, os nomes podem no mais fazer sentido, logo, precisam ser renomea-
dos. No obstante, os nomes do sistema em teste que forem renomeados tambm precisam ser
reetidos nos testes, no entanto, as ferramentas de refatorao no conseguem automatizar essa
tarefa, portanto, necessrio uma preocupao adicional quanto a isso.
Introduzir Varivel Explicativa: Alguns cenrios de testes podem criar cenrios complexos e no intu-
itivos. Para melhorar a legibilidade do cdigo dos testes, pode-se adicionar variveis temporrias
com nomes auto-explicativos para substituir expresses complexas.
6.3.3 Orientao a Objetos
Programas orientados a objetos possuem um bom potencial para alta testabilidade [117, 97, 27], prin-
cipalmente por causa das facilidades de modularizao atravs de classes, heranas e relacionamentos.
Entretanto, vrios detalhes de implementao dos objetos podem inuenciar no modo que os testes so
realizados.
Basicamente, o que precisa ser testado em um objeto sua interface, ou seja, tudo que exposto
para o resto da aplicao. A interface pblica de um objeto pode ser composta por variveis, mtodos e
classes internas, todas sujeitas a erros de implementao. Entretanto, o mais comum testar os mtodos
do objeto e das classes internas. Testes de variveis so apenas interessantes para o caso de constantes
ou para vericar se elas foram inicializadas corretamente.
Quando as funcionalidades pblicas de um objeto esto corretas, h bons indcios que toda a imple-
mentao interna do objeto tambm est. J quando o que est querendo ser testado alguma funcional-
idade interna (privada), preciso car atento, pois isso um indcio de que um conjunto de objetos no
possuem um bom design. Essa situao um exemplo tpico de como os testes automatizados provm
informaes para ajudar com a criao do design do sistema.
Nessa situao, o primeiro passo avaliar se o trecho em teste pode ser movido para algum outro
objeto mais coerente, de modo que a funcionalidade se torne pblica. Dado que o design est de acordo
com as necessidades do sistema, ento basta realizar os testes desejados. O empecilho que, devido s
limitaes das linguagens, pode ser impossvel os testes conseguirem fazer as chamadas das funcionali-
dades privadas. Em alguns casos, o uso de reexo pode ser suciente para chamada da funcionalidade.
Se ainda assim no for possvel, a soluo alterar a visibilidade da funcionalidade ou ento tentar
test-la de forma indireta, atravs de uma funcionalidade pblica que a utilize.
Para classes annimas, o pensamento anlogo ao de mtodos privados. Primeiro deve-se avaliar se
a classe precisa ser nomeada. Caso a classe precise mesmo ser annima, ento a soluo test-la de
forma indireta, ou seja, atravs da funcionalidade que a utiliza.
Ja para as classes abstratas que no podem ser instanciadas, a soluo mais simples, basta criar
uma subclasse concreta que no sobrescreva as funcionalidades da classe em teste. Para no poluir o
cdigo do sistema, essa classe deve ser acessvel apenas aos testes. No entanto, essa abordagem s
coerente se o sistema respeita o Princpio de Substituio de Liskov
4
[89]. Atravs dessa classe concreta
4
Esse princpio diz que uma instncia de uma subclasse deve ser capaz de substituir qualquer instncia da classe pai sem
qualquer dano ao sistema.
70
possvel vericar o cdigo implementado na classe pai, mas no possvel vericar se os mtodos
abstratos esto sendo chamadas corretamente. Para fazer essas vericaes, o recomendado utilizar
Objetos Dubls, tais como Objeto Emulado (Mock Object, Seo 6.4.6) ou Objeto Espio (Spy Object,
Seo 6.4.7).
Quanto aos recursos globais e mutveis, eles devem ser evitados sempre que possvel, assim como
tambm recomendado em programas procedimentais. No caso dos objetos, dois exemplos comuns
de recursos compartilhados so variveis de classe ou objetos Singleton. Todos recursos compartilhados
dicultam a automao dos testes e impedem a execuo paralela dos testes. Quando no possvel refa-
torar o sistema em teste, ento as baterias de testes devem ser executadas sequencialmente e, para evitar
testes intermitentes, cada conjunto de teste deve ser responsvel por atualizar o recurso compartilhado
de acordo com suas necessidades.
No obstante, as implementaes dos Padres de Projetos [61] tambm devem ser testadas, mesmo
que os programadores estejam bem familiarizados com o design dos objetos. Inclusive, tambm pos-
svel pensar em recomendaes para os testes das implementaes dos padres.
Por exemplo, para os padres Singleton e Flyweight recomendado que sejam vericadas que as
instncias dos objetos gerados sejam sempre as mesmas (assertSame), em oposio s instncias obti-
das atravs dos padres Prototype, Builder e Factory Method (assertNotSame). J os padres Chain
of Responsability e Composite lidam com colees de objetos, ento importante realizar testes com
colees vazias e com elementos.
Ainda, podem ser utilizados Objetos Dubls (6.2) para testar alguns Padres de Projeto. Por exem-
plo, Mediator, Interpreter, Adapter e Decorator podem exigir o uso de Objetos Espies para vericarem
que as sadas indiretas de dados esto corretas. No obstante, Template Method e Abstracty Factory po-
dem utilizar Objetos Falsicados para ajudar a testar o trecho concreto das implementaes. Outros
padres podem ser mais simples de serem testados por serem intrinsicamente mais coesos, tais como
Strategy, Command e State.
6.3.4 Orientao a Aspectos
Programao Orientada a Aspectos (POA) um novo paradigma de desenvolvimento que serve de com-
plemento Orientao a Objetos. Os Aspectos fornecem uma nova maneira de encapsular as fun-
cionalidades que so comuns a diversos objetos e difceis de serem isoladas [150]. O cdigo-fonte da
funcionalidade que foi modularizada em um Aspecto inserido em objetos pr-denidos, sendo que esse
processo pode ser feito em tempo de compilao ou execuo, dependendo das ferramentas utilizadas.
A POA uma tcnica poderosa, mas a criao de testes automatizados, tanto para os Aspectos como
para os sistemas que os utilizam, uma tarefa que requer novas abordagens. Um Aspecto no possui
uma identidade independente, pois ele depende de um objeto para existir; ou seja, ele no pode ser
instanciado diretamente. Entretanto, esse novo tipo de estrutura de dado pode conter diversos tipos de
erros, inclusive erros graves como de laos innitos. Por isso, fundamental que eles sejam muito bem
testados.
Existem muitos estudos e estratgias para se testar programas orientados a aspectos, sendo que al-
guns deles sugerem testes de unidade [86, 91] e outros de integrao [102, 83, 24, 106]. O teste de
unidade dene o prprio Aspecto como sendo uma unidade, e com a ajuda de ferramentas apropri-
adas (Jaml-Unit), criado uma maneira de fazer as vericaes diretamente. J os testes de integrao
vericam o comportamento dos objetos que receberam cdigo-fonte de Aspectos [150]. Contudo, o
termo integrao confuso para esse caso, pois possvel realizar testes de unidade para as classes que
possuem cdigo de Aspectos, que ser a estratgia abordada nessa seo.
Essa estratgia nada mais do que testar sistemas orientados a aspectos como se fossem simples-
mente orientado a objetos [152]. Dessa maneira, todas as prticas, padres e tcnicas conhecidas de
testes OO tambm podem ser utilizadas, inclusive o uso do Objeto Prottipo (Seo 6.4.8). Essa estrat-
71
gia coerente, pois um bom teste automatizado no testa como uma funcionalidade foi implementada,
mas sim o que ela deve fazer.
Um Aspecto composto de Pontos de Atuao (Pointcuts) e Adendos (Advices), sendo que ambos
precisam ser testados. As duas subsees seguintes discutiro as estratgias de cada um deles.
Pontos de Atuao (Pointcuts)
Os Pontos de Atuao so expresses que mapeiam os locais do sistema onde sero inseridos os trechos
de cdigo-fonte do aspecto (Adendos). Essas expresses so anlogas s expresses regulares, que pos-
suem padres de caracteres que devem ser encontrados em strings. Um padro denido de forma errada
pode adicionar ou remover pontos de atuao importantes [85]. Por isso, por mais que existam IDEs que
ajudam na criao dos Pontos de Atuao, fundamental ter uma bateria de testes automatizados para
evitar erros de distrao e de regresso.
Para testar esses pontos, pode ser utilizado Objetos Prottipo (Seo 6.4.8) que contenhamtrechos de
cdigo que devem e que no devem ser encontrados pelas expresses do Ponto de Atuao, justamente
para vericar os cenrios positivos e negativos. Para fazer as vericaes, pode ser utilizado Objetos
Espies ou Emulados, que possibilitam vericar se eles foram ou no exercitados. Consequentemente,
deve ser possvel injetar os Objetos Dubls para que os testes possam ser realizados.
Outra soluo a utilizao de arcabouos que ajudam a testar os Pontos de Atuao, como o APTE
(Automated Pointcut Testing for AspectJ Programs) [5]. Ele recebe um conjunto de aspectos e classes e
devolve duas listas: uma dos Pontos de Juno que satisfazemos Pontos de Atuao dos aspectos; e outra
contendo os Pontos de Juno que quase satisfazem os Pontos de Atuao, que so casos interessantes
de serem analisados em busca de erros. Dessa forma, basta criar um teste automatizado que mande
executar essa ferramenta e que verique se as listas contm ou no os mtodos esperados. Essa soluo
dispensa o uso de Objetos Dubls, mas ao mesmo tempo requer o uso de uma ferramenta externa a dos
testes.
Adendos (Advices)
J os Adendos so os trechos de cdigo que sero acrescentados em todos os Pontos de Juno (Join
points) do sistema, que so os locais que satisfazem os Ponto de Atuao do Aspecto. Isso implica
que erros de implementao em seu cdigo so espalhados em diversos pontos da aplicao. Conse-
quentemente, Aspectos mal implementados podem causar danos catastrcos ao sistema. Por isso,
fundamental a criao de uma bateria de testes automatizados muito cuidadosa.
A primeira recomendao para criao desses testes a utilizao do padro Objeto Humilde (Seo
6.4.2) para separar a lgica principal de outros detalhes de implementao no testveis. Por exem-
plo, em Java com AspectJ, parte do cdigo do Adendo pode estar escrito em Java (testvel) e parte na
linguagem do AspectJ (no testvel de forma direta sem a ajuda de arcabouos prprios).
O cdigo em Java pode ser encapsulado em um objeto comum e testado como qualquer outro.
Quanto ao restante da implementao, recomendvel que os testes sejam realizados, novamente,
atravs de Objetos Prottipo que receberam o cdigo do Adendo. A princpio, pode parecer que os
testes esto vericando o Objeto Dubl e no o sistema em teste, o que seria um erro, mas essa estrat-
gia coerente, pois o cdigo adicionado ao Objeto Dubl idntico ao cdigo adicionado ao sistema
em teste. Um exemplo de teste com Aspectos que engloba todos essas recomendaes pode ser visto na
Seo 6.4.8.
Ainda, caso o sistema possua diversos Aspectos que sero aplicados em um mesmo Ponto de Juno,
pode-se criar situaes de teste para vericar se existe incompatibilidade entre eles [119]. Para isso, basta
criar um Objeto Prottipo que satisfaz os Pontos de Atuao correspondentes.
Tambm importante notar que existem diversos tipos de Aspectos. Alguns apenas coletam in-
formaes do sistema e no inuenciam o resto da implementao do objeto, enquanto outros podem
72
alterar o uxo de controle ou at mesmo alterar os valores de variveis dos objetos [121]. Contudo, essa
estratgia permite criar testes para quaisquer dessas situaes, pois pensando no objeto como uma caixa
preta, ele no diferente. Apenas importante ressaltar que o cdigo dos Adendos deve permitir que os
objetos colaboradores possam ser injetados.
6.3.5 Reexo
Reexo a capacidade de um programa observar ou alterar sua prpria estrutura ou comportamento
[58]. A tcnica de programar utilizando essa capacidade tem sido vastamente utilizada, principalmente
por bibliotecas, APIs e engines. Bons exemplos so os arcabouos para programas Web, tais como os
populares Django (Python), Rails (Ruby), Grails (Groovy), Spring e Hibernate (Java).
No entanto, assim como a programao orientada a aspectos, as funcionalidades que utilizam re-
exo processam parte do prprio cdigo do sistema, por exemplo, por meio de bytecodes. Em alguns
casos, as regras de uma funcionalidade se baseiam em algumas caractersticas do cdigo, enquanto, em
outras situaes, novas funcionalidades so adicionadas a objetos em tempo de execuo. Para ambos os
casos podem haver diversos problemas, no s de correo, como tambm de segurana e desempenho.
Ainda, devem ser consideradas as inmeras possibilidades de se implementar um mesma soluo
computacional, portanto, mesmo os algoritmos reexivos mais simples devem se preocupar com muitos
detalhes. No obstante, o cdigo-fonte de sistemas pode estar em constante evoluo, seja atravs de
refatoraes, correes ou da adio de novas funcionalidades. Cada mudana do cdigo-fonte pode
quebrar os algoritmos reexivos, o que os tornam funcionalidades muito suscetveis a erros de regresso.
Por causa desses fatores, a automao de testes uma boa soluo para garantir a qualidade dessas
funcionalidades.
As recomendaes para criao de bons testes automatizados para os algoritmos reexivos so pare-
cidas aos de programao orientada a aspectos. Primeiramente, por mais que esses algoritmos no
tenham uma estrutura denida, uma boa prtica de programao separar as responsabilidades de re-
conhecimento de um padro de cdigo-fonte (anlogo aos Pontos de Atuao dos Aspectos) das tarefas
que sero realizadas no momento oportuno (anlogo aos Adendos dos Aspectos).
Para os testes das funcionalidades que reconhecem padres de cdigo-fonte, podem ser utilizados
Objetos Falsicados (Seo 6.2) ou Objetos Prottipo (Seo 6.4.8). J para as tarefas que sero execu-
tadas, pode-se utilizar os padres Objeto Humilde (Seo 6.4.2).
6.3.6 Mdulos Assncronos
Hoje, a importncia de sistemas assncronos muito grande devido Internet, grande modulariza-
o dos sistemas e ao uso de servios. Alm disso, a tendncia tornar os sistemas cada vez mais
independentes e velozes, devido criao e popularizao dos processadores com vrios ncleos e das
linguagens de programao que facilitam a escrita de sistemas altamente escalveis, tais como Erlang,
Haskell e Scala.
Trechos de cdigo que envolvem programao paralela ou distribuda so complexos e, por isso,
muito suscetveis a erros. H muitos pontos que precisam ser vericados quando um sistema utiliza
outros processos, threads ou atores, tais como a sincronizao e a comunicao atravs do compartil-
hamento de memria ou da troca de mensagens. Erros tpicos de programao concorrente como os
de sincronizao, deadlocks, livelocks, starvation e race conditions podem quebrar regras de negcio,
tornar dados inconsistentes e at mesmo deixar o sistema inteiro inutilizvel.
No entanto, os testes de unidade tem como premissa bsica a execuo sncrona do sistema em
teste. Apesar da grande importncia dos mdulos assncronos em sistemas de software, a maioria das
ferramentas e arcabouos de testes automatizados no possuem funcionalidades que facilitam a imple-
mentao de casos de teste de qualidade.
73
Contudo, criar testes automatizados para esses sistemas possvel, apesar de ser muito mais com-
plexo. Ao contrrio de uma bateria de testes sequenciais, a bateria de testes de mdulos assncronos
executada em uma thread diferente da do sistema em teste. Como a execuo do sistema testado depende
do sistema operacional e do escalonador de processos, no possvel prever, com exatido, quando o
sistema ser executado. Dessa forma, os testes no recebem automaticamente os efeitos colaterais do
sistema, logo, preciso que eles sejam sincronizados, ou ento, que observem quando o sistema foi
alterado, para ento fazer as vericaes no seu tempo.
Para sincronizar os testes necessrio o mesmo conhecimento e cautela da sincronizao do sistema
em teste. J para observar o sistema, os testes podem obter informaes de seu estado em um momento
oportuno, ou ento, eles podem capturar os eventos emitidos pelo sistema. Todas essas alternativas so
muito propcias a criarem testes com muitos antipadres, que geram os problemas de testes difceis de
manter e escrever, falsos positivos e negativos, testes pouco legveis, intermitentes e lentos.
A primeira boa prtica para criar bons testes de mdulos assncronos utilizar o padro Objeto
Humilde (Seo 6.4.2) para isolar a lgica de negcios da lgica computacional que gerencia a linha
de execuo da funcionalidade. A lgica de negcios deve ser testada como qualquer outra parte do
sistema. Quanto ao restante da funcionalidade, deve ser utilizado o padro Assert Eventually [60], que
verica periodicamente se a funcionalidade assncrona terminou de ser executada. Quando o padro
identica que o teste est pronto para ser testado, ento so feitas as vericaes da correo. Caso o
sistema demore muito para ser executado, ento o padro devolve um erro de tempo esgotado.
74
6.4 Padres
A seguir sero descritas solues de testes automatizados de unidade que podem ser aplicadas em difer-
entes contextos. Entretanto, algumas das recomendaes podem ser generalizadas para outros tipos de
teste. Vale ressaltar que todos os padres sero denidos segundo o esqueleto exibido na Seo 5.3.
6.4.1 Injeo de Dependncia (Dependency Injection)
Tipo: Testabilidade
Quando utilizar: Em sistemas orientado a objetos. Idealmente, em todos objetos do sistema.
Inteno: Desacoplar os objetos e facilitar a insero e substituio das suas dependncias.
Motivao: Sistemas muito acoplados so mais difceis de serem testados [142]. Mais especicamente,
objetos que no permitem a substituio de seus objetos colaboradores inviabilizam o uso de
Objetos Dubls para criao de testes isolados.
Soluo: Um objeto deve estar desacoplado de suas dependncias, de modo que haja mecanismos para
que todas elas possam ser substitudas por outros objetos do mesmo tipo. Consequentemente,
possvel testar o objeto isoladamente com o auxlio de Objetos Dubls.
Consequncias: Todos as dependncias podem ser inseridas atravs dos contrutores ou de mtodos per-
tinentes. Ainda, a responsabilidade de instanciar os objetos colaboradores passada para outros
objetos do sistema, ou at mesmo para novos objetos que so criados especicamente para isolar
esta responsabilidade, como ocorre quando utilizamos Padres de Projeto de Criao, tais como
Builder, Factory e Prototype [61].
Implementao: Para variveis privadas de instncia, a injeo das dependncias pode ser feita mais
comumente atravs do construtor, ou de mtodos set. Se a varivel for pblica, dentro de um
contexto, basta atualiz-la diretamente. Ainda, pode ser utilizado arcabouos que so responsveis
por vasculhar e inicializar as variveis desejadas, como o Spring para Java. J as dependncias no
associadas ao objeto em teste podem ser passadas como argumentos para os mtodos necessrios.
Exemplo - Java/JUnit/Hamcrest: A Figura 6.8 mostra um trecho de cdigo do objeto Compra de uma
loja que est acoplado s regras de desconto. Nesse exemplo, queremos testar apenas que o valor
do subtotal com desconto o valor do desconto subtrado do subtotal. Entretanto, da maneira que
est implementada, no conseguimos realizar esse teste sem conhecer toda a regra de descontos
aplicada na compra, isso porque a classe Compra e o mtodo que faz o clculo do subtotal com
desconto possuem mais de uma responsabilidade.
Testar funcionalidades que possuem mais de uma responsabilidade trabalhoso, alm de resultar
em diversos outros antipadres de testes automatizados. Isso porque os testes se tornam presos
implementao e no ao comportamento do sistema. A Figura 6.9 mostra um exemplo de teste
automatizado para o mtodo subTotalComDesconto.
O primeiro passo para melhorar a implementao do objeto Compra isolar as regras de desconto
em um objeto prpio para isso, como mostra a Figura 6.10. Contudo, essa melhoria no inuencia
em como os testes do objeto Compra sero implementados, isso porque o objeto colaborador
RegraDesconto ainda no pode ser injetado, dessa maneira, ainda necessrio conhecer as regras
de desconto para poder testar o mtodo subTotalComDesconto.
Para nalizar a refatorao, necessrio remover a responsabilidade do objeto Compra de instan-
ciar as regras de desconto (Figura 6.11), consequentemente, ser possvel injet-la tanto atravs
dos testes quanto pelo prprio sistema.
75
1 public class Compra {
2 Cliente cliente;
3 Produtos produtos;
4 FormaPagamento formaPagamento;
5 Promocao promocao;
6
7 public Compra(Cliente c, Produtos l, FormaPagamento f, Promocao p) {
8 this.cliente = c;
9 this.produtos = l;
10 this.pagamento = f;
11 this.promocao = p;
12 }
13
14 public Dinheiro subTotal() {
15 return produtos.subTotal();
16 }
17
18 // Objeto Desconto e as regras esto acoplados ao objeto Compra.
19 // No possvel testar o objeto Compra independente das regras de desconto.
20 public Dinheiro subTotalComDesconto() {
21 Dinheiro valor = new Dinheiro(0);
22
23 if(cliente.isVIP()) {
24 Desconto d = new DescontoVIP();
25 valor.add(d.valor(this));
26 }
27 else {
28 Desconto d = new DescontoPelaFormaDePagamento();
29 valor.add(d.valor(this));
30 valor.add(promocao.desconto().valor(this));
31 }
32
33 return subTotal().subtract(valor);
34 }
35 }
Figura 6.8: Objeto Compra com implementao acoplada ao objeto Desconto.
76
1 //Dependncias do JUnit + Hamcrest
2 import org.junit.Before;
3 import org.junit.Test;
4 import static org.junit.Assert.*;
5 import static org.hamcrest.Matchers.equalTo;
6
7 public class CompraTests {
8 // Objetos pertinentes ao teste
9 Produtos produtos;
10 Cliente cliente;
11 FormaPagamento pagamento;
12 Promocao promocao;
13 Compra compra;
14
15 @Before
16 public void setUp() {
17 produtos = new Produtos();
18 }
19
20 // Para realizar este teste preciso conhecer as regras de desconto.
21 // Um teste simples fica extenso e difcil de entender.
22 @Test public void totalComDescontoDeveSubtrairDescontoDoSubTotal() {
23 // Detalhes de implementao das regras de desconto.
24 boolean vip = true;
25 cliente = new Cliente(vip);
26 Dinheiro preco = new Dinheiro(100);
27 produtos.add(new Produto(preco));
28 pagamento = new BoletoFormaPagamento(); // 3% de desconto
29 promocao = new PromocaoDeNatal(); // 10% de desconto
30
31 compra = new Compra(cliente, produtos, pagamento, promocao);
32
33 // 100 - (3 + 10 + 15) = 72
34 assertThat(compra.subTotalComDesconto(), equalTo(new Dinheiro(72)));
35 }
36
37 // As regras de desconto devero ser testadas a partir do objeto Compra.
38 // Devero ser feitos testes para cliente no-VIP, com pagamento por carto etc.
39 }
Figura 6.9: Teste complicado do objeto Compra.
77
1 public class Compra {
2 Cliente cliente;
3 Produtos produtos;
4 FormaPagamento formaPagamento;
5
6 public Compra(Cliente c, Produtos l, FormaPagamento f) {
7 this.cliente = c;
8 this.produtos = l;
9 this.formaPagamento = f;
10 }
11
12 public Dinheiro subTotal() {
13 return produtos.subTotal();
14 }
15
16 // As regras de desconto esto isoladas no objeto RegraDeDescontoPadrao,
17 // mas ainda assim as regras esto implicitamente acopladas ao objeto Compra.
18 public Dinheiro subTotalComDesconto() {
19 RegraDesconto regraDesconto = RegraDescontoPadrao();
20 Dinheiro valorDesconto = regraDesconto.calcula(this);
21
22 return subTotal().subtract(valor);
23 }
24 }
Figura 6.10: Objeto Compra com implementao mais organizada, mas ainda acoplada ao objeto
Desconto.
A testabilidade est relacionada com a exibilidade do sistema, portanto, se est complicado de
testar, um indcio de que o sistema precisa ser refatorado. Com essa nova modelagem, o sistema
poder trabalhar com diversas regras de desconto simultneamente e os testes cam legveis e
fceis de implementar (Figura 6.12).
Padres Relacionados: O padro Objeto Humilde (Seo 6.4.2), que tambm utilizado para separar
as responsabilidades de um objeto, pode ser utilizado para desacoplar os objetos e facilitar a
injeo de dependncia. Ainda, esse padro pr-requisito para os padres Objeto Emulado
(Mock Object, Seo 6.4.6), Objeto Falsicado (Fake Object, Seo 6.4.5) e Objeto Espio (Test
Spy, Seo 6.4.7).
Usos Conhecidos: O arcabouo Spring para Java uma das ferramentas mais populares de injeo de
dependncia. Outro uso conhecido que merece destaque a API EJB para Java. Na Seo 10.3 h
outros exemplos relacionados.
Referncias: Existem livros especcos sobre injeo de dependncia [113] e outros de catlogo de
padres que tambm o descrevem [99].
78
1 public class Compra {
2 Cliente cliente;
3 Produtos produtos;
4 FormaPagamento formaPagamento;
5 RegraDesconto regraDesconto;
6
7 // possvel injetar as dependncias.
8 // Objeto Compra e Desconto no esto mais acoplados.
9 public Compra(Cliente c, Produtos p, FormaPagamento f, RegraDeDesconto r) {
10 this.cliente = c;
11 this.produtos = p;
12 this.formaPagamento = f;
13 this.regraDesconto = r;
14 }
15
16 public Dinheiro subTotal() {
17 return produtos.subTotal();
18 }
19
20 public Dinheiro subTotalComDesconto() {
21 Dinheiro valor = regraDesconto.calcula(compra);
22 return subTotal().subtract(valor);
23 }
24 }
Figura 6.11: Objeto Compra desacoplado de suas dependncias.
79
1 // Dependncias do JUnit + Hamcrest
2 import org.junit.Before;
3 import org.junit.Test;
4 import static org.junit.Assert.*;
5 import static org.hamcrest.Matchers.*;
6
7 public class CompraTests {
8 // Objetos Tolos
9 Cliente cliente = new Cliente();
10 FormaPagamento pagamento = new BoletoFormaPagamento();
11
12 // Objetos pertinentes ao teste
13 Produtos produtos;
14 Compra compra;
15 // Objeto Dubl: Objeto Falsificado
16 RegraDesconto regraDesconto = new RegraDesconto() {
17 public Dinheiro calcula(Compra compra) {
18 return new Dinheiro(15);
19 }
20 }
21
22 @Before
23 public void setUp() {
24 produtos = new Produtos();
25 }
26
27 // Valor total com desconto depende somente do valor dos produtos e
28 // do valor total do desconto.
29 @Test public void totalComDescontoDeveSubtrairDescontoDoSubTotal() {
30 Dinheiro preco = new Dinheiro(100);
31 produtos.add(new Produto(preco));
32
33 compra = new Compra(cliente, produtos, pagamento, regraDesconto);
34
35 // 100 - 15 = 85
36 assertThat(compra.subTotalComDesconto(), equalTo(new Dinheiro(85)));
37 }
38
39 // ... As regras de desconto tambm devem ser testadas, mas isoladamente e
40 // em outra classe de teste.
41 }
Figura 6.12: Teste do objeto Compra refatorado.
80
6.4.2 Objeto Humilde (Humble Object)
Tipo: Testabilidade
Quando utilizar: Sempre que o objeto em teste possui mais de uma responsabilidade, mas, princi-
palmente, quando difcil test-lo devido ao seu acoplamento com arcabouos ou at mesmo a
objetos complexos. So exemplos comuns os objetos que possuem processos assncronos, ou que
interagem com requisies Web e gerenciadores de bancos de dados.
Inteno: Uma boa prtica de orientao a objetos que cada objeto tenha apenas uma responsabil-
idade. A inteno desse padro justamente partir um objeto complexo em objetos simples e
coesos (objetos humildes).
Motivao: Objetos com muitas responsabilidades so difceis de serem testados. Primeiramente,
porque a inicializao do objeto pode car mais complexa, assim como os mtodos de preparao
do teste (set up). Alm disso, os prprios mtodos de testes tendem a car mais extensos e dif-
ceis de implementar, pois quanto mais acoplado um objeto est do resto do sistema, maior ser
o trabalho para executar um cenrio de teste de modo isolado. No obstante, quanto mais re-
sponsabilidades um objeto possui, mais vericaes so necessrias para avaliar a correo da
implementao.
Soluo: Refatorar objetos para que eles tenham apenas uma responsabilidade. Em particular,
necessrio separar a lgica testvel de um objeto dos aspectos tcnicos e complexos de arcabouos
e do ambiente.
Consequncias: Aps a refatorao, os objetos cam mais simples e coesos, pois possuem apenas uma
responsabilidade. Alm disso, a lgica de negcios ca desacoplada da lgica de infraestrutura,
tal como lgica de programao assncrona, persistncia de dados etc. Isso resulta em um sistema
mais exvel, com objetos coesos, desacoplados e com alta testabilidade.
Implementao: Esse padro descreve simplesmente uma boa prtica de orientao a objetos, indepen-
dentemente do sistema ter ou no testes automatizados. Sendo assim, no h uma implementao
sistematizada para esse padro. Qualquer padro arquitetural ou de projeto pode descrever a
soluo apropriada para melhorar a testabilidade dos objetos, assim como todas as tcnicas de
refatorao podem ser teis. No entanto, como as responsabilidades de um objeto sero divididas
entre objetos menores, natural a utilizao de refatoraes de extrao, tais como Extract Class
e Extract Method.
Exemplo - Python/Django: A Figura 6.13 possui um trecho de cdigo de uma aplicao Web com o
arcabouo Django para buscar pessoas por parte do nome. Essa funcionalidade recebe uma req-
uisio Web e devolve a resposta apropriada. No entanto, esse mtodo tambm possui a respons-
abilidade de gerar a query que ser executada no banco de dados para encontrar os resultados, ou
seja, alm do mtodo ter mais de uma responsabilidade, ele no segue a arquitetura MVC proposta
pelo arcabouo.
Essa falha de modelagem reete na qualidade dos testes automatizados. Para testar a busca re-
alizada no banco de dados necessrio lidar com objetos de requisio e de resposta Web. Para
testar apenas a query gerada, pode-se separar as responsabilidades em objetos distintos, como
mostrado na Figura 6.14. Nesse caso, o objeto PessoaManager o Objeto Humilde.
Padres Relacionados: Caso sejam feitos alguns testes nos objetos que contm os detalhes complexos
de arcabouos, o padro Injeo de Dependncia (Seo 6.4.1) pode ser utilizado para substituir
o Objeto Humilde por dubls.
81
1 # Funcionalidade para buscar pessoas por parte do nome.
2 def busca_pessoa_pelo_nome(request):
3 if request.method == POST:
4 pessoas = Pessoa.objects.filter(nome__icontains=request.POST[texto_busca
])
5 else:
6 pessoas = []
7 return HttpResponse(/pessoas-encontradas, {pessoas: pessoas})
Figura 6.13: Exemplo de funcionalidade com muitas responsabilidades.
1 # Objeto Humilde: Para testar, basta chamar o mtodo
2 # Pessoa.objects.com_parte_do_nome com uma string desejada.
3 # Os testes no precisam mais lidar com objetos de Request e Response.
4 class PessoaManager(models.Manager):
5 def com_parte_do_nome(texto_busca):
6 return self.filter(nome__icontains=texto_busca)
7
8
9 # Mtodo refatorado
10 def busca_pessoa_pelo_nome(request):
11 if request.method == POST:
12 pessoas = Pessoa.objects.com_parte_do_nome(request.POST[texto_busca])
13 else:
14 pessoas = []
15 return HttpResponse(/pessoas-encontradas, {pessoas: pessoas})
Figura 6.14: Funcionalidade de busca de pessoas refatorada, utilizando um Objeto Humilde.
82
Referncias: Esse padro foi identicado por Meszaros [99].
83
6.4.3 Objeto Tolo (Dummy Object)
Tipo: Testabilidade
Quando utilizar: Quando for necessrio lidar com objetos que no so utilizados pelo cenrio de teste,
mas que so fundamentais para sua execuo. Por exemplo, para evitar erros de compilao.
Inteno: Apenas viabilizar a execuo de um teste.
Motivao: Para realizao de um caso de teste, pode ser necessrio a instanciao de vrios obje-
tos, mas nem sempre todos eles so utilizados diretamente pela funcionalidade em teste. Alguns
desses objetos so necessrios apenas para evitar erros de compilao ou de execuo de outras
funcionalidades que no esto sob vericao.
Soluo: Substituir os objetos colaboradores que so necessrios para a execuo de um teste mas que
no so processados por objetos nulos ou implementados da maneira mais simples possvel.
Consequncias: Torna vivel a execuo dos cenrios de teste.
Implementao: Basta substituir os objetos colaboradores dispensveis por valores nulos ou instncias
implementadas da maneira mais simples e legvel possvel.
Exemplo - Java/JUnit/Mockito: mais comum que os Objetos Tolos sejam objetos de tipos primi-
tivos das bibliotecas provenientes da linguagem de programao utilizada (strings, nmeros etc),
entretanto, tambm pode acontecer de serem de tipos denidos pelo prprio sistema em teste.
A Figura 6.15 mostra trs maneiras comentadas de substituir objetos colaboradores por Objetos
Tolos.
Padres Relacionados: Quando no for possvel passar valores nulos e a inicializao do Objeto Tolo
se torna complexa, ento recomendado o uso de Objetos Emulados (Mock Objects, Seo 6.4.6).
Usos Conhecidos: O uso desse padro natural durante a implementao de um caso de teste.
84
1 class Pessoa {
2 public Pessoa(String nome, Date nascimento) { /* ... */ }
3 public int idade() { /* mtodo em teste */ }
4 }
5
6 import java.util.Date;
7
8 // Dependncias do JUnit + Hamcrest
9 import org.junit.Test;
10 import static org.junit.Assert.*;
11
12 // Dependncias do Mockito
13 import static org.mockito.Mockito.*;
14
15 public class PessoaTests {
16
17 @Test public void idadeDeUmaPessoaQueNasceuHojeRetornaZero_versao1() {
18 // A String nome recebe um valor nulo.
19 // Se o nome receber algum processamento, como validao de dados,
20 // essa abordagem se torna invivel.
21 Pessoa pessoa = new Pessoa(null, new Date());
22 assertEquals(0, pessoa.idade());
23 }
24
25 @Test public void idadeDeUmaPessoaQueNasceuHojeRetornaZero_versao2() {
26 // A String "Um nome qualquer" um Objeto Tolo.
27 // O clculo da idade no deve depender do nome da pessoa.
28 Pessoa pessoa = new Pessoa("Um nome qualquer", new Date());
29 assertEquals(0, pessoa.idade());
30 }
31
32 @Test public void idadeDeUmaPessoaQueNasceuHojeRetornaZero_versao3() {
33 // interessante deixar claro quando um objeto no deve
34 // interferir no teste.
35 // A biblioteca Mockito fornece alguns mtodos com esse propsito,
36 // tais como o anyString, anyObject, anyInt...
37 Pessoa pessoa = new Pessoa(anyString(), new Date());
38 assertEquals(0, pessoa.idade());
39 }
40 }
Figura 6.15: Exemplo de Objeto Tolo.
85
6.4.4 Objeto Stub (Test Stub)
Tipo: Testabilidade
Quando utilizar: Quando os dados obtidos de objetos colaboradores inuenciam e dicultam a criao
de testes automatizados para uma funcionalidade.
Inteno: Substituir objetos colaboradores que so difceis de serem manipulados por verses que
possam ser controladas. Dessa maneira, o objeto pode ser congurado para construir diferentes
cenrios de teste.
Motivao: Muitos objetos colaboradores so difceis de serem manipulados, consequentemente, os
testes se tornam difceis de serem realizados. Por exemplo, os que envolvem o relgio do com-
putador, datas etc.
Soluo: Criar objetos que so fceis de serem manipulados para substituir o comportamento que prej-
udica a testabilidade.
Consequncias: A testabilidade do sistema melhorada, o que possibilita a simulao de diversos
cenrios de teste.
Implementao: Deve-se criar uma variao do objeto colaborador, seguindo a mesma interface, mas
de modo que ele seja capaz de retornar dados controlados para a funcionalidade em teste. Ento
esse objeto deve ser injetado no objeto em teste.
Padres Relacionados: O padro Injeo de Dependncia (Dependency Injection, Seo 6.4.1)
necessrio para injetar o Objeto Stub no objeto em teste. J o Objeto Emulado (Mock Object,
Seo 6.4.6) tambm atua como esse padro fornecendo dados estticos para o objeto em teste,
no entanto, o Objeto Emulado tambm possui funcionalidades que permitem vericar chamadas
indiretas da funcionalidade em teste.
Usos Conhecidos: A soluo proposta por esse padro surgiu antes da soluo proposta pelos Objetos
Emulados [28, 93].
86
6.4.5 Objeto Falsicado (Fake Object)
Tipo: Testabilidade
Quando utilizar: O Objeto Falsicado uma soluo elegante para realizao de testes difceis de
serem simulados. Por exemplo, quando queremos vericar o comportamento do sistema quando
ocorre problemas de hardware, rede, sistemas de arquivos etc. Esse padro tambm til para re-
solver problemas de testes de partes do sistema que dependem de mdulos intrinsicamente lentos.
Entretanto, os Objetos Emulados (Seo 6.4.6) tambm servem para solucionar esses problemas,
com a vantagem de que so mais fceis de serem utilizados do que a soluo proposta por este
padro. Sendo assim, esse padro s deveria ser utilizado quando no existir uma biblioteca de
Objetos Emulados apropriada para as tecnologias utilizadas pelo sistema em teste.
No entanto, os Objetos Falsicados so capazes de fornecer dados gerados dinamicamente, en-
quanto os Objetos Emulados no so apropriados para isso. Por isso, os Objetos Falsicados so
mais interessantes para testes que precisam de uma grande quantidade de dados, tais como testes
de sanidade.
Outra situao que requer gerao de dados dinmica ocorre quando o teste de correo feito
atravs da comparao dos resultados de algoritmos similares (Seo 6.4.9). Essa abordagem
especialmente til quando est sendo feito uma otimizao: os resultados gerados por um algo-
ritmo que sabemos que est correto so utilizados como valores esperados do algoritmo que est
sendo testado.
Inteno: Fornecer uma implementao simplicada e isolada de uma dependncia da funcionalidade
em teste (objeto colaborador) para que um cenrio de teste se torne vivel de ser realizado.
Motivao: Alguns cenrios de testes so difceis de serem criados ou executados, principalmente os
que dependem de regras de negcio complexas, servios externos ao sistema em teste etc.
Soluo: A ideia fornecer uma implementao auxiliar e exclusiva para os testes de uma funcional-
idade do sistema, de modo que facilite a realizao dos cenrios de testes que utilizam indireta-
mente essa funcionalidade. A proposta a mesma do Antipadro Gancho para os Testes (Test
Hook, Seo 6.5.1), mas a implementao deve ser feita de modo elegante, ou seja, sem poluir e
aumentar a complexidade do sistema em teste. O cdigo auxiliar deve ser visvel apenas dentro
do escopo dos testes automatizados.
Consequncias: Os cenrios de testes difceis de serem realizados de forma isolada se tornam simples
como quaisquer outros.
Implementao: Os Objetos Falsicados devem possuir a mesma interface do objeto colaborador a
ser substitudo, mas com uma implementao simplicada do comportamento esperado. A im-
plementao pode ser desde uma verso limitada de um algoritmo at mesmo um conjunto de
informaes hard-coded que so simplesmente retornadas. Para a simulao de erros, a imple-
mentao pode simplesmente lanar a exceo adequada.
Exemplo - Python/UnitTest: A classe Compra, citada no exemplo do padro Injeo de Dependncia
(Dependency Injection, Seo 6.4.1) possui um exemplo de Objeto Falsicado de uma implemen-
tao com dados estticos (Figura 6.12), o que bem simples de implementar.
Padres Relacionados: Esse padro a soluo elegante do Antipadro Gancho para os Testes (Test
Hook, Seo 6.5.1). Tambm pode-se utilizar Objetos Falsicados para implementar o padro
Teste por Comparao de Algoritmos (Seo 6.4.9). J o padro Objeto Emulado (Mock Object,
87
Seo 6.4.6) prope um outra soluo para resolver parte dos problemas que esse padro tam-
bm se prope a resolver. Por m, como todo Objeto Dubl, o padro Injeo de Dependncia
(Dependency Injection, Seo 6.4.1) fundamental.
88
6.4.6 Objeto Emulado (Mock Object)
Tipo: Testabilidade
Quando utilizar: Objetos Emulados tambm atuam como Objetos Stub, fornecendo dados para o ob-
jeto em teste atravs dos objetos colaboradores. Por isso, eles tambm podem ser utilizados
rotineiramente durante a criao dos cenrios de teste. Quando a criao e congurao dos ob-
jetos colaboradores uma tarefa complexa, ento deve-se utilizar preferencialmente esse padro,
que facilita essas tarefas. Esse padro tambm possui similaridades com o Objeto Espio. Am-
bos armazenam informaes do que foi executado, o que permite que sejam feitas vericaes no
comportamente interno da funcionalidade em teste.
Inteno: Possibilitar e facilitar a criao de testes para um objeto de forma isolada. Esse padro
tambm permite vericar as chamadas indiretas da funcionalidade em teste.
Motivao: Testar um cdigo no trivial de maneira isolada difcil. Alm disso, criar e congurar
objetos Objetos Stub e Espio pode ser uma tarefa complexa.
Soluo: O Objeto Emulado cria uma implementao vazia do objeto colaborador e permite que o
comportamento de cada mtodo do objeto possa ser descrito de forma dinmica.
Consequncias: As funcionalidades so testadas isoladamente. Alm disso, os Objetos Emulados so
muito rpidos, o que melhora a performance dos testes.
Implementao: Criar um Objeto Emulado no uma tarefa trivial, pois ele feito de maneira
dinmica por meio de reexo. Por isso, s vivel sua utilizao se existir uma biblioteca
de Objetos Emulados para a linguagem do sistema em teste.
Padres Relacionados: Esse padro atua como o Objeto Stub (Test Stub, Seo 6.4.4), fornecendo
dados para o objeto em teste. Tambm permite vericar chamdas indiretas, como o Objeto Es-
pio (Test Spy, Seo 6.4.7). Os Objetos Emulados tambm precisam ser injetados no objeto em
teste, por isso o padro Injeo de Dependncia (Dependency Injection, Seo 6.4.1) tambm
importante.
Usos Conhecidos: Essa soluo foi identicada no ano 2000 [93] e desde ento temsido muito estudada
[138, 80].
89
6.4.7 Objeto Espio (Test Spy)
Tipo: Testabilidade
Quando utilizar: Quando o que se est querendo vericar algum comportamento interno da fun-
cionalidade em teste, que no se reete diretamente nos resultados obtidos. Em outras palavras,
o efeito colateral produzido pela funcionalidade em teste no pode ser vericado atravs de um
valor de retorno ou de uma exceo lanada. Exemplos tpicos so testes de classes abstratas
(especialmente comum em APIs) e de sistemas de registros (log).
Inteno: Permitir que um teste consiga vericar se uma chamada indireta de uma funcionalidade est
sendo executada corretamente.
Motivao: importante vericar a correo de chamadas indiretas. Elas podem conter no apenas
detalhes fundamentais para o funcionamento do sistema, como tambm podem interferir na cor-
reo do comportamento explcito de uma funcionalidade. Contudo, no possvel vericar esse
tipo de funcionalidade do modo convencional, atravs dos efeitos colaterais diretos causados.
Soluo: Criar um objeto que coleta informaes das chamadas indiretas da funcionalidade em teste
para que possam ser utilizadas posteriormente para vericao.
Consequncias: Comobjetos espies se torna possvel vericar a correo das sadas indiretas de dados
de uma funcionalidade, incluindo chamadas de mtodos abstratos.
Implementao: As chamadas indiretas podem ser do prprio objeto em teste ou de algum objeto
colaborador. Para o primeiro caso, deve-se herdar a classe em teste acrescentando um sistema
de registro dos mtodos executados. J para o segundo caso, deve-se fazer o mesmo mas com o
objeto colaborador, com o trabalho adicional de que o objeto espio dever ser injetado no objeto
em teste. Contudo, existem bibliotecas que facilitam o trabalho de gerar objetos espies, alm de
fornecerem funes padronizadas para vericao dos dados coletados.
Exemplo - Python/Unittest: A Figura 6.16 mostra uma classe abstrata com um algoritmo de sin-
cronizao que percorre duas listas simultaneamente enquanto vai comparando seus itens
(SincronizadorDeListas). Os tipos que herdarem dessa classe devem denir qual o comporta-
mento desejado quando os tens forem comuns a ambas as listas ou exclusivo em alguma delas.
importante notar que no possvel testar o comportamento dessa classe do modo convencional,
comparando as sadas diretas de dados (valores retornados ou excees lanadas) com valores es-
perados. Uma soluo criar uma classe espi que adicionar um comportamento para capturar
informaes das chamadas indiretas de modo que essas informaes possam ser vericadas poste-
riormente. A Figura 6.17 contm uma implementao de classe espi e realiza os testes usando-a
como base.
Exemplo - Python/Unittest/Python-Mockito: A principal vantagem de utilizar ferramentas de objetos
espies que o trabalho de criar um novo objeto com um sistema de registro dispensado. Outra
vantagem que os testes cam padronizados e, consequentemente, mais fceis de serem interpre-
tados. A Figura 6.18 mostra um outro exemplo de teste para o cdigo da Figura 6.16, mas dessa
vez utilizando a ferramenta Python-Mockito para gerar os objetos espies.
Padres Relacionados: O padro Injeo de Dependncia (Dependency Injection, Seo 6.4.1) fun-
damental para que o Objeto Espio seja inserido no objeto em teste. O Objeto Emulado (Mock
Object, Seo 6.4.6) tambm armazena informaes da sua execuo, no entanto, ele no capaz
de fornecer dados dinmicos para o sistema em teste.
90
1 class SincronizadorDeListas(object):
2
3 def __init__(self, lista1, lista2):
4 self.lista1 = lista1
5 self.lista2 = lista2
6 self.lista1.sort()
7 self.lista2.sort()
8
9 def executa(self):
10 self.__executa_recusivamente(0, 0)
11
12 def __executa_recusivamente(self, indice1, indice2):
13 sem_mais_elementos_na_lista1 = indice1 >= len(self.lista1)
14 sem_mais_elementos_na_lista2 = indice2 >= len(self.lista2)
15
16 if sem_mais_elementos_na_lista1 and sem_mais_elementos_na_lista2:
17 return
18
19 if sem_mais_elementos_na_lista2:
20 elemento1 = self.lista1[indice1]
21 self.processa_elemento_exclusivo_lista1(elemento1)
22 return self.__executa_recusivamente(indice1 + 1, indice2)
23
24 if sem_mais_elementos_na_lista1:
25 elemento2 = self.lista2[indice2]
26 self.processa_elemento_exclusivo_lista2(elemento2)
27 return self.__executa_recusivamente(indice1, indice2 + 1)
28
29 elemento1 = self.lista1[indice1]
30 elemento2 = self.lista2[indice2]
31
32 if elemento1 == elemento2:
33 self.processa_elementos_iguais(elemento1, elemento2)
34 return self.__executa_recusivamente(indice1 + 1, indice2 + 1)
35 elif elemento1 > elemento2:
36 self.processa_elemento_exclusivo_lista2(elemento2)
37 return self.__executa_recusivamente(indice1, indice2 + 1)
38 else: # elemento1 < elemento2
39 self.processa_elemento_exclusivo_lista1(elemento1)
40 return self.__executa_recusivamente(indice1 + 1, indice2)
41
42 # mtodos abstratos:
43 def processa_elementos_iguais(self, elemento1, elemento2): pass
44 def processa_elemento_exclusivo_lista1(self, elemento1): pass
45 def processa_elemento_exclusivo_lista2(self, elemento2): pass
Figura 6.16: Uma classe python com mtodos abstratos.
91
1 import unittest
2
3 from sincronizador import SincronizadorDeListas
4
5 # Herda a classe em teste e adiciona comportamento para espion-la.
6 class SincronizadorDeListasEspiao(SincronizadorDeListas):
7
8 def __init__(self, lista1, lista2):
9 super(SincronizadorDeListasEspiao, self).__init__(lista1, lista2)
10 self.processa_elementos_iguais_contador = 0
11 self.processa_elemento_exclusivo_lista1_contador = 0
12 self.processa_elemento_exclusivo_lista2_contador = 0
13
14 def processa_elementos_iguais(self, element1, element2):
15 self.processa_elementos_iguais_contador += 1
16
17 def processa_elemento_exclusivo_lista1(self, element1):
18 self.processa_elemento_exclusivo_lista1_contador += 1
19
20 def processa_elemento_exclusivo_lista2(self, element2):
21 self.processa_elemento_exclusivo_lista2_contador += 1
22
23
24 class SincronizadorDeListasEspiaoTests(unittest.TestCase):
25
26 def verificar_chamadas(self, espiao, a, b, c):
27 self.assertEquals(a, espiao.processa_elementos_iguais_contador)
28 self.assertEquals(b, espiao.processa_elemento_exclusivo_lista1_contador)
29 self.assertEquals(c, espiao.processa_elemento_exclusivo_lista2_contador)
30
31 def test_nao_deve_executar_nada_se_recebe_duas_listas_vazias(self):
32 espiao = SincronizadorDeListasEspiao([], [])
33 espiao.executa()
34 self.verificar_chamadas(espiao, 0, 0, 0)
35
36 def test_deve_processar_elemento_exclusivo_lista1(self):
37 espiao = SincronizadorDeListasEspiao([1], [])
38 espiao.executa()
39 self.verificar_chamadas(espiao, 0, 1, 0)
40
41 def test_deve_processar_elemento_exclusivo_lista2(self):
42 espiao = SincronizadorDeListasEspiao([], [1])
43 espiao.executa()
44 self.verificar_chamadas(espiao, 0, 0, 1)
45
46 def test_deve_processar_elemento_comum_em_ambas_as_listas(self):
47 espiao = SincronizadorDeListasEspiao([1], [1])
48 espiao.executa()
49 self.verificar_chamadas(espiao, 1, 0, 0)
50
51 def test_ambas_as_listas_com_elementos_distintos(self):
52 espiao = SincronizadorDeListasEspiao([1], [2])
53 espiao.executa()
54 self.verificar_chamadas(espiao, 0, 1, 1)
Figura 6.17: Exemplo de teste com Objeto Espio.
92
1 import unittest
2 from mockito import *
3
4 from sincronizador import SincronizadorDeListas
5
6 class SincronizadorDeListasTests(unittest.TestCase):
7
8 def test_nao_deve_executar_nada_se_recebe_duas_listas_vazias(self):
9 espiao = spy(SincronizadorDeListas([], []))
10 espiao.executa()
11
12 verify(espiao).executa()
13 verify(espiao).__executa(0, 0)
14 verifyNoMoreInteractions(espiao) # No executou nenhum outro mtodo
15
16 def test_deve_processar_elemento_exclusivo_lista1(self):
17 espiao = spy(SincronizadorDeListas([1], []))
18 espiao.executa()
19
20 # No executou o mtodo processa_elementos_iguais com parmetro 1
21 verify(espiao, times=0).processa_elementos_iguais(1)
22 # Executou uma vez o mtodo processa_elemento_exclusivo_lista1 com
parmetro 1
23 verify(espiao, times=1).processa_elemento_exclusivo_lista1(1)
24 verify(espiao, times=0).processa_elemento_exclusivo_lista2(1)
25
26 def test_deve_processar_elemento_exclusivo_lista2(self):
27 espiao = spy(SincronizadorDeListas([], [1]))
28 espiao.executa()
29
30 verify(espiao, times=0).processa_elementos_iguais(1)
31 verify(espiao, times=0).processa_elemento_exclusivo_lista1(1)
32 verify(espiao, times=1).processa_elemento_exclusivo_lista2(1)
33
34 def test_deve_processar_elemento_comum_em_ambas_as_listas(self):
35 espiao = spy(SincronizadorDeListas([1], [1]))
36 espiao.executa()
37
38 verify(espiao, times=1).processa_elementos_iguais(1)
39 verify(espiao, times=0).processa_elemento_exclusivo_lista1(1)
40 verify(espiao, times=0).processa_elemento_exclusivo_lista2(1)
41
42 def test_ambas_as_listas_com_elementos_distintos(self):
43 espiao = spy(SincronizadorDeListas([1], [2]))
44 espiao.executa()
45
46 verify(espiao, times=0).processa_elementos_iguais(1)
47 verify(espiao, times=0).processa_elementos_iguais(2)
48 verify(espiao, times=1).processa_elemento_exclusivo_lista1(1)
49 verify(espiao, times=0).processa_elemento_exclusivo_lista1(2)
50 verify(espiao, times=0).processa_elemento_exclusivo_lista2(1)
51 verify(espiao, times=1).processa_elemento_exclusivo_lista2(2)
Figura 6.18: Exemplo de teste de Objeto Espio com Python-Mockito.
93
Usos Conhecidos: As ferramenta Mockito para Java e Python-Mockito para Python so ferramentas
que disponibilizam objetos espies.
94
6.4.8 Objeto Prottipo
Tipo: Organizacional, Robustez e Testabilidade
Quando utilizar: Quando uma funcionalidade processa trechos arbitrrios de cdigo-fonte, como
ocorre quando utilizado Reexo ou Programao Orientada a Aspectos.
Inteno: Agrupar em um ou poucos objetos de uso exclusivo dos testes, diferentes prottipos de im-
plementao que sejam pertinentes para realizao dos cenrios de teste de uma funcionalidade
que trabalha com informaes do cdigo-fonte.
Motivao: Pode ser necessrio o uso de muitos objetos distintos e no relacionados para testar fun-
cionalidades que processam cdigo-fonte, principalmente quando elas so muito abrangentes. Por
exemplo, um aspecto pode ser denido para adicionar cdigo a todas as classes do sistema. Isso
pode causar diferentes tipos de problema para os testes, como set ups complexos, testes comlgica
condicional, entre outros problemas organizacionais.
Alm disso, testes que envolvem muitos mdulos de um sistema no so robustos. Alteraes de
cdigo-fonte em diversos pontos do sistema podem quebrar os testes, mesmo que a funcionalidade
que est sendo testada no tenha sido alterada.
Ainda h os casos emque os testes no podemser realizados devido indisponibilidade de objetos.
Por exemplo, comum que as APIs forneam objetos abstratos e incompletos, contendo apenas
um esqueleto de implementao (classes abstratas e Template Methods). Para esses casos, pode
ser utilizado Prottipos de Objetos em Teste ou outros Objetos Dubls.
Soluo: Os testes podem criar um ou mais objetos, visveis apenas no escopo dos testes (Objetos
Dubls), que contm prottipos de diferentes tipos de implementao, de modo que diferentes
cenrios de testes possam ser realizados.
Diferentemente dos Objetos Falsicados (Seo 6.2), o Objeto Prottipo no precisa respeitar
uma API denida rigidamente pelo sistema, a no ser que seja pertinente ao teste. De maneira
geral, ele apenas deve fornecer recursos que imitem as caractersticas de cdigo-fonte dos objetos
do sistema. Ainda, o prottipo no fornece dados ao sistema, o prprio cdigo-fonte compe as
informaes necessrias para realizao dos testes.
Outra grande diferena entre o Objeto Prottipo e outros Objetos Dubls o processo de instalao
do objeto no sistema em teste. No caso de orientao a aspectos, uma forma de instalao se d
atravs da compilao do cdigo dos testes utilizando o compilador de aspectos. Isso adicionar
o comportamento a ser testado no prottipo. J para reexo, a instalao ainda pode ser feita do
modo convencional, atravs de injeo de dependncia.
importante ressaltar que o que deve ser testado so funcionalidades do sistema, nunca os Objetos
Dubls. Sendo assim, no a implementao do prottipo que deve ser utilizada para os testes,
mas sim as informaes do seu cdigo-fonte ou trechos de cdigo que foram embutidos.
Consequncias: Todos os tipos de cdigo-fonte necessrios para os testes cam encapsulados em um
ou poucos objetos no escopo dos testes. Isso deixa os testes mais organizados, simples e robustos.
Implementao: No caso de orientao a aspectos, o prottipo deve ser um objeto simples, mas que
seja identicado pelos Pontos de Atuao (Pointcuts, Seo 6.3.4) para que os testes consigam ex-
ercitar o cdigo dos Adendos (Advices) que sero adicionados ao prottipo. Entretanto, tambm
interessante criar prottipos que no sejam identicados pelos Pontos de Atuao, justamente
para testar que eles realmente no so encontrados pelos aspectos. Para reexo, a implementao
similar, mas o objeto deve satisfazer alguma API para que as funcionalidades reexivas recon-
heam o objeto. Da mesma maneira, prottipos incompatveis com as funcionalidades tambm
95
so recomendados para realizao dos testes de casos de erros. Valor notar que, dependendo da
funcionalidade, os nomes das classes e mtodos podem descrever o que eles representam para os
testes.
Exemplo - Java: A Figura 6.19 mostra um Objeto Prottipo que pode ser utilizado para testes de difer-
entes aspectos. Se um Ponto de Atuao representa apenas os mtodos protegidos (protected),
ento o cdigo do Adendo deve ser inserido apenas no mtodo umMetodoProtegidoParaTeste.
Se o comportamento desse mtodo corresponder ao cdigo do Adendo, ento a expresso
de mapeamento do Ponto de Atuao est encontrando o trecho de cdigo desejado. No
entanto, tambm necessrio vericar que o Adendo no inserido nos outros mtodos.
Ainda, os testes do prprio Adendo pode ser vericado atravs do comportamento do mtodo
umMetodoProtegidoParaTeste.
1
2 public class PrototipoDeObjetoEmTeste {
3 public void umMetodoPublicoParaTeste() {}
4
5 protected void umMetodoProtegidoParaTeste() {}
6
7 private void umMetodoPrivadoParaTeste() {}
8
9 public void umMetodoQueLancaExcecao() throws Exception {
10 throw new Exception();
11 }
12 // ...
13 }
Figura 6.19: Objeto Prottipo.
Exemplo - Python/Unittest/Django-Dynamic-Fixture: A biblioteca Django-Dynamic-Fixture utiliza
reexo para identicar os tipos de dados esperados para cada varivel de um objeto de dados do
arcabouo Django. Depois que os tipos so identicados, a biblioteca preenche as variveis com
informaes apropriadas e do tipo esperado. A Figura 6.20 mostra alguns testes dessa ferramenta
utilizando esse padro.
Padres Relacionados: O padro Objeto Humilde 6.4.2 deve ser sempre utilizado como pr-requisito,
justamente para simplicar os casos de testes e, consequentemente, o Objeto Prottipo.
Ainda, como o objeto criado um Objeto Dubl (Seo 6.2), existem similaridades entre ele
e Objetos Stub, Mock, Falsicado e Tolo. Por exemplo, a implementao visvel apenas ao
escopo dos testes, assim como a implementao deve ser simples, fcil e rpida.
Usos Conhecidos: A ferramenta Util4Selenium, que usa aspectos (AspectJ com Java) para gerar fo-
tograas das interfaces Web, usa esse padro para testar a funcionalidade. J a biblioteca de
testes Python-QAssertions e a engine de jogos de cartas Card Game Engine tambm utilizam essa
soluo para testar os trechos de cdigo que envolvem reexo.
96
1 from django.test import TestCase
2 from django.db import models
3
4 # O mtodo new preenche as variveis do objeto com valores vlidos
5 from django_dynamic_fixture import new
6
7 class MetodoNewPreencheInstanciaComDadosTest(TestCase):
8
9 def test_preenche_integer_fields_com_inteiros(self):
10 # Objeto Prottipo
11 class UmModelo(models.Model):
12 integer_field = models.IntegerField()
13 instancia = new(UmModelo)
14 # O mtodo new identificou que a varivel esperava receber um nmero
inteiro
15 self.assertTrue(isinstance(instancia.integer_field, int))
16
17 def test_preenche_char_fields_com_strings(self):
18 # Objeto Prottipo
19 class UmModelo(models.Model):
20 char_field = models.CharField(max_length=10)
21 instancia = new(UmModelo)
22 # O mtodo new identificou que a varivel esperava receber uma string
23 self.assertTrue(isinstance(instancia.char_field, str))
Figura 6.20: Exemplo em Python de testes da biblioteca Django-Dynamic-Fixture utilizando o padro
Objeto Prottipo.
97
6.4.9 Teste por Comparao de Algoritmos
Tipo: Qualidade
Quando utilizar: Quando a funcionalidade em teste um algoritmo de alta complexidade com muitas
combinaes de dados de entrada e sada, enquanto existem algoritmos mais simples que resolvem
o mesmo problema, mas que so inviveis de serem utilizados em ambiente de produo. Por
exemplo, quando alguns algoritmos triviais para problemas complexos so extremamente lentos.
Esse padro especialmente til para vericar algoritmos de clculo de propriedades matemticas,
anlise combinatria, programao musical, computao grca etc.
Inteno: Vericar a correo de um algoritmo otimizado com base nos valores gerados por um outro
algoritmo reconhecidamente correto.
Motivao: Muitos problemas computacionais so difceis de serem resolvidos, ainda mais se o de-
sempenho e a exibilidade da soluo for imprescindvel. Alm do mais, algoritmos complexos
podem conter muitas parties de domnios, dicultando ou mesmo inviabilizando a criao de
uma bateria de testes automatizados que traga segurana quanto correo da implementao.
Soluo: Testar o algoritmo com uma grande quantidade de dados de entrada com o intuito de encontrar
algum cenrio de teste que ainda no foi vericado. Para isso, deve ser implementado um algo-
ritmo, que seja correto e fcil de implementar, para gerar os resultados esperados de um caso de
teste que sero posteriormente utilizados para comparar com os resultados obtidos pelo algoritmo
em teste.
Consequncias: O algoritmo testado com uma grande quantidade de dados, o que traz mais segurana
quanto sua correo, j que aumenta as chances de vericar alguma partio de domnio que o
desenvolvedor pode ter esquecido de testar.
Implementao: O primeiro passo implementar um algoritmo reconhecidamente correto dentro do
escopo dos testes. A prxima etapa programar para que tanto o algoritmo de controle, quanto o
algoritmo em teste sejam executados diversas vezes com os mesmos dados de entrada, para que
ento seja comparado os resultados obtidos pelos dois.
Exemplo: Na Figura 6.21 temos um algoritmo eciente para clculo do Mximo Dividor Comum
(M.D.C.) de dois nmeros inteiros. Os primeiros cenrios de testes a serem realizados devem
ser os casos mais simples, tais como combinaes de nmeros pares, mpares, nmeros primos e
nmeros primos entre si. Caso haja insegurana quanto correo do algoritmo, podemos imple-
mentar um teste de comparao de algoritmos para tentar encontrar um cenrio de teste que ainda
no foi pensado. Para esse caso, podemos utilizar um algoritmo lento, mas reconhecidamente
correto, para servir de modelo para os testes (Figura 6.22).
98
1 class MathHelper(object):
2 # Algoritmo de Euclides: mdc(a, b) = mdc(b, r) onde r: a = q * b + r
3 def mdc(self, a, b):
4 valor = max([a, b])
5 divisor = min([a, b])
6 resto = valor % divisor
7 while resto != 0:
8 valor = divisor
9 divisor = resto
10 resto = valor % divisor
11 return divisor
Figura 6.21: Algoritmo eciente para clculo do M.D.C. entre dois nmeros inteiros.
1 import random
2 import unittest
3
4 class MathHelperFake(object):
5 # Algoritmo ingenuamente lento, mas simples de implementar.
6 def mdc(self, a, b):
7 maior_divisor_possivel = min((a, b))
8 divisor_comum = 1
9 for i in range(1, maior_divisor_possivel+1):
10 if a % i == 0 and b % i == 0:
11 divisor_comum = i
12 return divisor_comum
13
14 class MathHelperTest(unittest.TestCase):
15 def setUp(self):
16 self.fake = MathHelperFake()
17 self.math_helper = MathHelper()
18
19 def test_aleatorio_por_comparacao_de_algoritmo_do_calculo_de_mdc(self):
20 for i in range(1, 10):
21 a = random.randint(1, 1000000)
22 b = random.randint(1, 1000000)
23 expected_value = self.fake.mdc(a, b)
24 value = self.math_helper.mdc(a, b)
25 self.assertEquals(expected_value, value)
Figura 6.22: Exemplo de Teste por Comparao de Algoritmos.
99
6.4.10 Teste por Probabilidade
Tipo: Robustez e Qualidade
Quando utilizar: Quando o que est sendo testado possui comportamento aleatrio. Esta situao
tpica em jogos e algoritmos de segurana, tais como gerao de senhas aleatrias e algoritmos de
criptograa que utilizam, em alguma etapa, nmeros pseudo-aleatrios.
Inteno: Denir o resultado nal de um teste baseado nos resultado de diversas execues de um teste
intermitente.
Motivao: Funcionalidades que produzem resultados aleatrios so difceis de serem testadas, pois os
resultados esperados no so previsveis. Tentar prever os resultados leva ao indcio de antipadro
Testes Intermitentes (Seo 5.2).
Soluo: Executar um mesmo caso de teste diversas vezes e denir se o teste aceito de acordo com a
porcentagem de sucesso.
Consequncias: denido, de forma vivel, o resultado de um caso de teste baseado na probabilidade
de sucesso de uma vericao. Os testes continuaro sendo teoricamente intermitentes, mas, na
prtica, robusto como outro qualquer.
Implementao: Antes de utilizar este padro, importante refatorar o sistema ou tentar utilizar o
padro Objeto Humilde (Seo 6.4.2) para isolar o comportamento aleatrio de uma funcionali-
dade. Isso poder facilitar a escrita dos Testes por Probabilidade ou at mesmo evit-los.
Dado que necessrio utilizar esse padro, ento necessrio executar o teste uma quantidade
determinada de vezes (um lao simples) e armazenar os resultados de todas as execues (uma
lista com os resultados), para no nal, calcular a razo de sucesso sobre fracasso e comparar com a
probabilidade desejada (uma conta e comparao simples). Idealmente, esse algoritmo deve estar
integrado ao arcabouo de teste para seguir as convenes e enriquecer o relatrio da bateria dos
testes, embora seja possvel implement-lo de maneira independente, como mostrado na Figura
6.23.
Exemplo - Java/JUnit/TestNG: Aimplementao no complexa, mas pode tornar o cdigo dos testes
obscuro, outro indcio de antipadro. Por isso, importante abstrair o conceito do Teste por
Probabilidade de forma que que transparente para os testes que o utilizam. Contudo, existem
arcabouos que j fornecem essa implementao e facilitam a utilizao deste padro. A Figura
6.24 mostra um exemplo de teste de uma funcionalidade tpica em jogos de cartas, embaralhar
uma pilha de cartas. O exemplo escrito em Java e utiliza o arcabouo TestNG, que permite criar
facilmente testes por probabilidade atravs de metadados.
Padres Relacionados: O padro Objeto Humilde (Seo 6.4.2) til para separar o que for possvel
da lgica aleatria do resto da lgica de uma funcionalidade. Tambm, Testes por Probabilidade
podem ser usados em conjunto com Testes de Sanidade (Seo 3.4.3) para possibilitar fazer ver-
icaes mais rgidas, assim, o padro tambm pode ser utilizado com o intuito de garantir a
qualidade do sistema.
Usos Conhecidos: O arcabouo de testes TestNG fornece uma maneira simples de congurar testes
baseados na probabilidade de sucesso.
100
1 public class TestePorProbabilidadeImplTests {
2
3 public void testePorProbabilidade() {
4 // Implementao do teste
5 }
6
7 // Teste ser executado 20 vezes.
8 // Se 8 ou mais vezes (>= 80%) passar, resultado encarado como sucesso.
9 // Caso contrrio, encarado como falha.
10 @Test
11 public void testePorProbabilidadeDecorator() {
12 int QTDE_EXECUCOES = 20;
13 int PORCENTAGEM_DE_SUCESSO_ESPERADA = 80;
14
15 int quantidadeDeSucessos = 0;
16 for(int i = 0; i < 20; i++) {
17 try {
18 testePorProbabilidade();
19 quantidadeDeSucessos++;
20 } catch(Exception e) {
21 }
22 }
23 int porcentagemDeSucesso = 100 * quantidadeDeSucessos / QTDE_EXECUCOES;
24 if(porcentagemDeSucesso < PORCENTAGEM_DE_SUCESSO_ESPERADA)
25 throw new RuntimeException(
26 "Falhou mais do que o esperado: " +
27 porcentagemDeSucesso + "%");
28 }
29 }
Figura 6.23: Exemplo de teste que verica a correo de um teste pela probabilidade.
101
1 //Referncias do TestNG
2 import org.testng.annotations.Test;
3 //Referncias do JUnit + Hamcrest
4 import static org.junit.Assert.*;
5 // Custom Matcher: isNotSorted
6 import static QAMatchers.*;
7
8 public class PilhaDeCartasTests {
9
10 // Teste ser executado 20 vezes.
11 // Se 8 ou mais vezes (>= 80%) passar, resultado encarado como sucesso.
12 // Caso contrrio, encarado como falha.
13 @Test(invocationCount = 20, successPercentage = 80)
14 public void embaralharDeveMisturarAsCartasEmUmaOrdemAleatoria() {
15 PilhaDeCartas pilhaDeCartas = new PilhaDeCartas();
16 pilhaDeCartas.adiciona(new Carta(1, 1));
17 pilhaDeCartas.adiciona(new Carta(2, 2));
18 pilhaDeCartas.adiciona(new Carta(3, 3));
19 pilhaDeCartas.embaralha();
20 // Verifica que NO est ordenado.
21 // Intermitente: A funo embaralha pode deixar o baralho organizado
22 assertThat(pilhaDeCartas.getCartas(), isNotSorted(Carta.comparadorPorNaipeValor
()));
23 }
24 }
Figura 6.24: Exemplo de teste que verica a correo de um teste pela probabilidade.
102
6.4.11 Vericar Inversibilidade
Tipo: Qualidade
Quando utilizar: Ao testar duas funes do sistema que precisam ser exatamente uma inversa da outra.
Tipicamente em testes de funes matemticas bijetoras, funcionalidades de importao e expor-
tao de dados, voltar e refazer aes, alm de efeitos de imagem e som que so inversveis.
Inteno: Vericar a existncia de erros de incompatibilidade entre uma funo bijetora e sua inversa.
Motivao: Duas funcionalidades podem ser individualmente corretas, mas ao trabalharem como inver-
sas so incompatveis. Vrios erros podem ser cometidos, especialmente em pequenos detalhes
que passam despercebidos, tais como caracteres invisveis em strings, diferenas de arredonda-
mento em pontos utuantes e at mesmo a ordem de elementos idnticos em listas ordenadas, que
podem ser diferentes quando so utilizados algoritmos de ordenao instveis.
Soluo: Criar cenrios de testes que comparem os resultados produzidos por duas funes que so
inversas entre si, sendo que os dados de entrada de uma das funes so os dados de sada da
outra.
Consequncias: vericado se as implementaes de duas funes teoricamente inversas entre si po-
dem ser utilizadas na prtica, sem incompatibilidade.
Implementao: A ideia do teste executar as duas funcionalidades supostamente inversas entre si
f e f
1
, sendo que uma processar um dado qualquer x e a outra receber como dados de entrada
os dados de sada produzida pela primeira, f (x). Assim, temos: y = f
1
( f (x)). Se x = y, ento o
teste tido como sucesso, caso contrrio, uma falha.
Exemplo - Java/JUnit/Hamcrest: A Figura 6.25 mostra um algoritmo simples de criptograa e de-
scriptograa, que so duas funcionalidades necessariamente inversas. Caso contrrio, o usurio
poder perder dados criptogrados importantes porque no conseguir recuper-los. Note que,
propositalmente, um detalhe da implementao do mtodo de descriptograa foi comentado para
enfatizar o caso de teste de inversibilidade (Figura 6.26).
Exemplo - Python/UnitTest/QAssertions: A implementao do teste de inversibilidade simples de
ser feito e no suja o cdigo-fonte. No entanto, possvel criar mtodos de assero que abstraem
o objetivo do teste, o que til para enfatizar o que est sendo testado e tambm para lembrar e
incentivar a realizao deste tipo de teste. A Figura 6.27 mostra um exemplo com a ferramenta
Python-QAssertions. O mtodo de assero recebe os dois mtodos que teoricamente so funes
inversas entre si e os argumentos que sero passados para as funes em si. A ferramenta executa
os dois mtodos apropriadamente e faz as comparaes apropriadas.
Padres Relacionados: Se a quantidade de argumentos que podem ser passados para as funes in-
nita, ento impossvel provar com testes automatizados, que duas funcionalidades so inversas
entre si. Por isso, interessante que sejam feitas vericaes com diversos argumentos. Para isso,
pode-se utilizar os padres Teste de Sanidade (Seo 3.4.3) e Testes Aleatrios (Seo 3.4.1) para
gerar argumentos para vericao de inversibilidade.
Usos Conhecidos: A ferramenta Python-QAssertions implementa um mtodo de assero
(assertFunctionsAreInversible) que realiza este tipo de vericao.
103
1 import java.util.Random;
2
3 public class Criptografador {
4 private static final int _RANDOM_NUMBER = 10;
5
6 public String criptografar(String senha, String texto) {
7 Random random = new Random(senha.hashCode());
8 byte[] bytes = texto.getBytes();
9 for(int i = 0; i < bytes.length; i++) {
10 bytes[i] = (byte) (bytes[i] + random.nextInt(_RANDOM_NUMBER));
11 }
12 return "!!" + new String(bytes) + "!!";
13 }
14
15 public String descriptografar(String senha, String texto) {
16 // Precisa descomentar a linha abaixo para que
17 // as funes criptografar e decriptografar sejam inversas entre si.
18 // texto = texto.replaceFirst("^!!", "").replaceFirst("!!$", "");
19 Random random = new Random(senha.hashCode());
20 byte[] bytes = texto.getBytes();
21 for(int i = 0; i < bytes.length; i++) {
22 bytes[i] = (byte) (bytes[i] - random.nextInt(_RANDOM_NUMBER));
23 }
24 return new String(bytes);
25 }
26 }
Figura 6.25: Algoritmo ingnuo de criptografar and descriptografar textos.
104
1 //Referncias do JUnit + Hamcrest
2 import org.junit.*;
3 import static org.junit.Assert.*;
4 import static org.hamcrest.Matchers.*;
5
6 public class CriptografiaSimplesTests {
7 Criptografador c;
8
9 @Before
10 public void inicializaVariaveis() {
11 c = new CriptografadorSimples();
12 }
13
14 @Test // Sucesso.
15 public void testeCriptografar() {
16 String texto = "abcdefghijk";
17 String criptografado = c.criptografar("senha123", texto);
18 assertThat(criptografado, equalTo("!!jddhfgkplmk!!"));
19 }
20
21 @Test // Sucesso.
22 // Dependendo do requisito do cliente, pode ser um falso positivo.
23 public void testeDescriptografar() {
24 String criptografado = "jddhfgkplmk";
25 String decriptografado = c.decriptografar("senha123", criptografado);
26 assertThat("abcdefghijk", equalTo(decriptografado));
27 }
28
29 @Test // Falha.
30 // As funes criptografar e descriptografar no so inversas entre si.
31 public void criptografarInversaDeDescriptografar() {
32 String texto = "abc";
33 String criptografado = c.criptografar("senha123", texto);
34 String descriptografado = c.descriptografar("senha123", criptografado);
35 assertThat(texto, equalTo(descriptografado));
36 }
37 }
Figura 6.26: Teste de inversibilidade dos algoritmos de criptograa e descriptograa.
1 # Referncia do UnitTest
2 import unittest
3 # Referncia do Python-QAssertions
4 import qassertions as qa
5
6 class InversibilidadeTests(unittest.TestCase):
7
8 def testFuncaoCriptInversaDeDecript(self):
9 c = Criptador("senha123")
10 # qa.assertFunctionsAreInversible(mtodo1, mtodo2, argumento)
11 qa.assertFunctionsAreInversible(c.cript, c.decript, "texto")
Figura 6.27: Assero de Inversibilidade da ferramenta Python-QAssertions.
105
6.4.12 Vericar Valores Limites
Tipo: Qualidade
Quando utilizar: Emtestes de algoritmos que trabalhamcomintervalos de valores de dados de entrada,
sada ou, at mesmo, de variveis locais. O uso tpico em algoritmos com clculos puramente
matemticos, laos complexos, vetores e matrizes, conjuntos de dados ordenaveis, pontos utu-
antes etc.
Inteno: Vericar erros de programao causados pelos valores limites dos intervalos de dados que o
algoritmo trabalha.
Motivao: Estudos mostram que erros em valores limites so frequentes [95]. Erros de validao de
dados, falhas de segmentao, laos innitos, diviso por zero etc. Alm disso, erros de valores
limites podem ser acarretados devido interpretao incorreta de requisitos denidos de forma
ambgua ou no clara pelos clientes.
Soluo: Testar as funcionalidades de acordo com os dados extremos de entrada e sada.
Consequncias: feito a preveno contra erros causados por valores extremos.
Implementao: A implementao dos testes de valores limites no necessitam de solues especi-
ais, apenas os valores de entrada e sada so escolhidos cuidadosamente para forar clculos e
comparaes especcos.
Exemplo - C/CUnit: A Figura 6.28 mostra uma funcionalidade escrita em C que faz a multiplicao
de matrizes de inteiros. A implementao, apesar de curta, possui muitos detalhes sutis, o que
aumenta as chances do programador cometer erros conceituais ou por distrao, tais como trocas
de variveis e erros de precedncia de operadores. Alm do mais, essa uma funo que aceita
uma combinao innita de dados de entrada, o que torna impossvel provar sua correo atravs
de testes automatizados, embora possvel criar uma boa bateria de testes que d segurana na
funcionalidade.
1 /*
2 Matrizes de inteiros A, B e C
3 C(m x o) = A(m x n) * B(n x o)
4 Retorna 0 se sucesso, negativo caso contrrio
5 */
6 int matrizXmatriz(int **A, int m, int n, int **B, int o, int **C){
7 int i, j, k;
8 if(m < 1 || n < 1 || o < 1) return -1;
9 for(i = 0; i < m; i++) {
10 for(j = 0; j < o; j++) {
11 C[i][j] = 0;
12 for(k = 0; k < n; k++)
13 C[i][j] = C[i][j] + (A[i][k] * B[k][j]);
14 }
15 }
16 }
Figura 6.28: Funo escrita em C que calcula a multiplicao de matrizes.
Para essa funcionalidade, problemas de valores limites podem ocorrer tanto com os dados de
entrada como os de sada. A Figura 6.29 mostra alguns dos testes que podem ser feitos para
106
vericar esse tipo de erro. O exemplo utiliza a ferramenta CUnit, mas mostra apenas os cenrios
de teste. No Apndice B h mais informaes sobre a ferramenta, em particular, como executar
os testes com o arcabouo.
1 /* Referncias do CUnit, outras referncias foram ocultas */
2 #include <CUnit/CUnit.h>
3
4 /* Funes auxiliares: Implementao oculta para simplificar o exemplo */
5 /* A = B = C|0 ... 0|
6 |0 ... 0| ... */
7 void inicializaMatrizesZeradas(int** A, int m, int n, int** B, int o, int** C) {}
8
9 /* A|a b| B|e f| C|0 0|
10 |c d| |g h| |0 0| */
11 void inicializaMatrizesDoisPorDoisComValores(int** A, int a, int b, int c, int d,
int** B, int e, int f, int g, int h, int** C) {}
12
13 void verificaMatrizDoisPorDois(A, a, b, c, d) {
14 CU_ASSERT_EQUAL(A[0][0], a);
15 CU_ASSERT_EQUAL(A[0][1], b);
16 CU_ASSERT_EQUAL(A[1][0], c);
17 CU_ASSERT_EQUAL(A[1][1], d);
18 }
19
20 /* TESTES */
21
22 /* Funo se comporta bem com zeros? */
23 void test_ValoresLimitesDosCalculos(void) {
24 int **A, **B, **C;
25 inicializaMatrizesDoisPorDois(A, 0, 1, 0, 1, B, 1, 0, 1, 0, C);
26 int conseguiuCalcular = matrizXmatriz(A, 2, 2, B, 2, C);
27 CU_ASSERT_TRUE(conseguiuCalcular == 0);
28 verificaMatrizDoisPorDois(C, 1, 1, 1, 1);
29 }
30
31 /* E com nmeros negativos? */
32 void test_ValoresLimitesDosCalculos(void) {
33 int **A, **B, **C;
34 inicializaMatrizesDoisPorDois(A, 1, -1, 1, -1, B, -1, 1, -1, 1, C);
35 int conseguiuCalcular = matrizXmatriz(A, 2, 2, B, 2, C);
36 CU_ASSERT_TRUE(conseguiuCalcular == 0);
37 verificaMatrizDoisPorDois(C, 1, 1, 1, 1);
38 }
39
40 /* Resultados (dados de sada) devem pertencer ao intervalo dos inteiros */
41 void test_ValoresLimitesDosResultados(void) {
42 int **A, **B, **C;
43 inicializaMatrizesDoisPorDois(A, INT_MAX, INT_MAX, INT_MAX, INT_MAX, B, 2, 2, 2,
2, C);
44 int conseguiuCalcular = matrizXmatriz(A, 2, 2, B, 2, C);
45 CU_ASSERT_TRUE(conseguiuCalcular < 0);
46 }
Figura 6.29: Teste da multiplicao de matrizes usando a biblioteca CUnit.
Exemplo - Scala/JUnit/TestNG: Contudo, esses tipos de erros podem acontecer at com algoritmos
mais simples e que utilizam linguagens de programao de mais alto nvel. A Figura 6.30 mostra
107
um exemplo de testes escritos em Scala com JUnit e TestNG para o jogo de carta Poker. As regras
do jogo denem uma hierarquia simples de combinaes de cartas, mas que programaticamente
pode conter erros nos valores limites das regras.
1 // Referncias do TestNG e JUnit
2 import org.testng.annotations._
3 import org.junit.Assert._
4 // Imports das classes do sistema foram ocultos
5
6 // Exemplos de testes de valores limites para o jogo de cartas Poker
7 // Obs: Nomes dos mtodos de testes definem parte das regras do jogo
8 class ComparacaoEntreCombinacoesLimitesTests {
9
10 @Test def menorParGanhaDeMaiorCartaMaisAlta() {
11 assertTrue(menorPar() > maiorCartaMaisAlta())
12 assertTrue(maiorCartaMaisAlta() < menorPar())
13 }
14
15 @Test def menorDoisParesGanhaDeMaiorPar() {
16 assertTrue(menorDoisPares() > maiorPar())
17 assertTrue(maiorPar() < menorDoisPares())
18 }
19
20 @Test def menorTrincaGanhaDeDeMaiorDoisPares() {
21 assertTrue(menorTrinca() > maiorDoisPares())
22 assertTrue(maiorDoisPares() < menorTrinca())
23 }
24
25 @Test def menorSequenciaGanhaDeDeMaiorTrinca() {
26 assertTrue(menorSequencia() > maiorTrinca())
27 assertTrue(maiorTrinca() < menorSequencia())
28 }
29
30 @Test def menorTodasMesmoNaipeGanhaDeDeMaiorSequencia() {
31 assertTrue(menorTodasMesmoNaipe() > maiorSequencia())
32 assertTrue(maiorSequencia() < menorTodasMesmoNaipe())
33 }
34
35 @Test def menorFullHouseGanhaDeDeMaiorTodasMesmoNaipe() {
36 assertTrue(menorFullHouse() > maiorTodasMesmoNaipe())
37 assertTrue(maiorTodasMesmoNaipe() < menorFullHouse())
38 }
39
40 @Test def menorQuadraGanhaDeDeMaiorFullHouse() {
41 assertTrue(menorQuadra() > maiorFullHouse())
42 assertTrue(maiorFullHouse() < menorQuadra())
43 }
44
45 @Test def menorSequenciaTodasMesmoNaipeGanhaDeDeMaiorQuadra() {
46 assertTrue(menorSequenciaTodasMesmoNaipe() > maiorQuadra())
47 assertTrue(maiorQuadra() < menorSequenciaTodasMesmoNaipe())
48 }
49 }
Figura 6.30: Teste escrito em Scala dos valores limites das regras do Poker.
Exemplo - Python/UnitTest/QAssertions: Ainda, existem ferramentas que geram testes automatiza-
dos para vericar valores limites em regras de validao de dados de entrada. o caso da
108
Python-QAssertions, que fornece o mtodo de vericaao assertValidation. Esse mtodo
recebe como argumentos o prprio mtodo em teste e os parmetros que sero utilizados para
sua execuo. Se os parmetros forem objetos herdados da classe ValidationTest (Min, Max,
Positive, Negative, Range, InList, NotInList, Blank e NonBlank), ento o algoritmo iden-
tica que necessrio vericar valores limites para determinado parmetro, de acordo com o tipo
de validao.
Por exemplo, se um parmetro for Range(56, 790) (Figura 6.31), sero gerados os testes para
valores os 56 e 790, onde esperado sucesso, ou seja, nenhuma exceo lanada. Tambm so
feito testes para os valores 55 e 791, para os quais so esperadas excees. No obstante, ainda
so feitos testes com outros valores menos signicativos, tais como 423 (esperado sucesso), 46 e
800 (esperado falha).
1 # Referncias do UnitTest
2 import unittest
3 # Referncias do Python-QAssertions
4 import qassertions as qa
5 from qassertions import Range
6
7 class UmaClasseDoSistemaTests(unittest.TestCase):
8
9 def setUp(self):
10 self.sistema = UmaClasseDoSistema()
11
12 def testValorTemQueSerMaiorOuIgualQue56MenorOuIgualQue790(self):
13 qa.assertValidation(self.sistema.metodoEmTeste, Range(56, 790))
Figura 6.31: Exemplo de vericao de validao com casos limites com gerao de casos de teste.
AFigura 6.32 mostra como poderiamcar a implementao dos testes semusar a gerao de casos
de teste de validao. Mesmo para os testes de validao de apenas umparmetro a implementao
ca mais extensa. Consequentemente, quanto mais parmetros precisam ser validados, maior a
vantagem do uso da ferramenta. A Figura 6.33 mostra um exemplo de um teste de validao de
vrios argumentos. imporante notar que os valores limites so denidos subtraindo e somando 1
dos limites do intervalo, mas tambm pode-se alterar a preciso para outros valores, como mostra
a validao Max no exemplo da gura.
Usos Conhecidos: A teoria de testes de software incentiva o uso desse padro [95]. A ferramenta
Python-QAssertions que fornece um mtodo de assero que gera testes de valores limites. API
para criao de jogos de cartas Card Game Engine, que realiza esses testes para vericar erros de
implementao.
109
1 # Referncias do UnitTest
2 import unittest
3 # Referncias do Python-QAssertions
4 import qassertions as qa
5
6 class UmaClasseDoSistemaTests(unittest.TestCase):
7
8 def setUp(self):
9 self.sistema = UmaClasseDoSistema()
10
11 def testValorTemQueSerMaiorOuIgualQue56MenorOuIgualQue790(self):
12 self.assertRaises(Exception, self.sistema.metodoEmTeste, 46)
13 qa.assertDontRaiseAnException(self.sistema.metodoEmTeste, 56)
14 self.assertRaises(Exception, self.sistema.metodoEmTeste, 55)
15 qa.assertDontRaiseAnException(self.sistema.metodoEmTeste, 423)
16 qa.assertDontRaiseAnException(self.sistema.metodoEmTeste, 790)
17 self.assertRaises(Exception, self.sistema.metodoEmTeste, 791)
18 self.assertRaises(Exception, self.sistema.metodoEmTeste, 800)
Figura 6.32: Exemplo de vericao de validao com casos limites sem gerao dos casos de teste.
1 # Referncias do UnitTest
2 import unittest
3 # Referncias do Python-QAssertions
4 import qassertions as qa
5 # Tipos de validao:
6 # Nmeros: Min, Max, Positive, Negative, Range
7 # Listas: InList, NotInList
8 # Strings: Blank, NonBlank
9 from qassertions import Min, Max, Range, InList, NotInList, Blank, NonBlank
10
11 class UmaClasseDoSistemaTests(unittest.TestCase):
12
13 def setUp(self):
14 self.sistema = UmaClasseDoSistema()
15
16 def testValidacaoDeVariosDadosDeEntrada(self):
17 qa.assertValidation(self.sistema.metodoEmTeste2,
18 Min(5), Max(7, 0.1), valor sem regra de validao,
19 Range(5, 10), InList([1, 5, 7]))
Figura 6.33: Exemplo de vericao de validao com casos limites para diversos parmetros.
110
6.5 Antipadres
A seguir sero descritos antipadres de automao para testes de unidade, de acordo com o esqueleto
denido na Seo 5.4. Os indcios de problemas citados nos padres possuem mais informaes na
Seo 5.2.
6.5.1 Gancho para os Testes (Test Hook)
Tipo: Testabilidade
Contexto: Testar um sistema pode ser muito complexo, principalmente quando os mdulos e objetos
esto muito acoplados entre si, como discutido na Seo 10.3. Quando no possvel substituir
nem parte do comportamento do sistema atravs de Objetos Dubls, os testes podem se tornar
inviveis de serem realizados. Uma soluo simples modicar o prprio comportamento do
sistema apenas para a execuo dos testes, ou seja, feito um gancho no sistema para torn-lo
testvel.
Apesar de simples, essa soluo ruim porque ela polui o cdigo do sistema, tornando-o mais ex-
tenso e complexo. Isso aumenta as chances do sistema conter erros, que vai completamente contra
o objetivo dos testes automatizados. No obstante, essa soluo ainda pode inibir refatoraes para
melhorar o design do sistema.
Exemplo: A implementao tpica a criao de uma ag para indicar que so os testes que esto
executando o sistema. Se forem os testes, ento executado o trecho de cdigo substituto e
testvel do sistema, como exibido na Figura 6.34.
1 // Antipadro: Gancho para os Testes (Test Hook)
2 public class UmaClasseDoSistema {
3 public boolean TESTANDO = false;
4 // ...
5 public void umaFuncionalidadeComBaixaTestabilidade() {
6 if(TESTANDO) {
7 // Simule algo de forma isolada e controlada para os testes ...
8 }
9 else {
10 // Faa o que deve ser feito ...
11 }
12 }
13 // ...
14 }
Figura 6.34: Antipadro Gancho para os Testes.
Indcios de Problemas Relacionados: Hard-to-Test Code, Test Logic in Production e Production Bugs
(vide Seo 5.2).
Referncias: Esse antipadro foi identicado por Meszaros como um padro [99]. No entanto, ele
explica que essa abordagem deve ser utilizada apenas em casos excepcionais, quando invivel
refatorar o sistema para aumentar a testabilidade.
111
6.5.2 Testes Encadeados (Chained Tests)
Tipo: Organizacional
Contexto: normal que alguns casos de testes sejam parecidos e possuam trechos de cdigo auxiliares
em comum. Por isso, refatoraes para diminuir a replicao de cdigo-fonte deve ser uma tarefa
rotineira durante a criao dos testes automatizados.
Uma situao comum, que umcaso de teste seja muito parecido como mtodo de set up de outro,
isso porque os testes podem ser complementares. Uma possvel soluo para evitar replicao de
cdigo para este caso, fazer com que ambos os testes compartilhem as mesmas variveis e sejam
executados em sequncia, de forma encadeada.
Entretanto, essa soluo incentiva o uso de informaes compartilhadas entre os testes e, conse-
quentemente, dependentes entre si, frgeis, difceis de serem entendidos e mantidos. Alm disso,
essa abordagem pode at inviabilizar o uso de ferramentas que otimizam a bateria de testes como
um todo, por meio de ferramentas que paralelizam a execuo dos testes. Se uma funcionalidade
dependente de outra, pode-se utilizar Objetos Dubls para tornar os testes independentes.
Exemplo/Java/TestNG: A Figura 6.35 mostra um exemplo de teste com a ferramenta TestNG que
possui funcionalidades para criar testes encadeados.
1 //Referncias do TestNG
2 import org.testng.annotations.Test ;
3
4 public class UmaClasseDeTest {
5
6 @Test
7 public void teste1() {
8 }
9
10 // Antipadro: Testes Encadeados (Chained Tests)
11 // Esse teste s ser executado depois do que o teste1 tenha sido executado.
12 @Test(dependsOnMethods="teste1")
13 public void teste2() {
14 }
15 }
Figura 6.35: Um exemplo de esqueleto de cdigo Java do antipadro Testes Encadeados.
Indcios de Problemas Relacionados: Obscure Test, Assertion Roulette, Erratic Test, Fragile Test,
Frequent Debugging, Slow Tests, Buggy Tests, High Test Maintenance Cost (vide Seo 5.2).
Referncias: Esse antipadro foi identicado por Meszaros como um padro [99]. Em casos excep-
cionais, esse antipadro pode ser til em testes de integrao, mas desde que ele seja utilizado
com cautela.
112
Captulo 7
Testes com Persistncia de Dados
Controlar e garantir a qualidade da camada de persistncia de dados fundamental, no apenas devido
ao valor da informao, mas tambm porque dados inconsistentes podem afetar a correo das outras
camadas do sistema [64].
Falhas com os dados podem desencadear uma innidade de situaes indeterminadas de erros no
sistema que gerencia a camada de persistncia. As camadas superiores ao mdulo de dados so imple-
mentadas para trabalhar com dados corretos, ou seja, no esto preparadas para lidar com situaes onde
os dados no seguem os formatos e as convenes denidas.
Quando esses erros so identicados antes da causa principal do problema, o processo de identi-
cao e correo das falhas se torna um trabalho ainda mais complexo e demorado. O prprio sistema,
ou at mesmo o usurio nal, pode ser induzido a criar novos dados problemticos, o que, talvez, resulte
em um ciclo automtico de adio de erros e na perda da credibilidade dos dados armazenados.
Por causa da gravidade que os problemas nos dados podem ter, muitas empresas possuem depar-
tamentos exclusivos de especialistas para implementar e gerenciar a camada de persistncia de dados.
Contudo, mesmo os especialistas na rea e as ferramentas de persistncia de dados no garantem que
todas as situaes propcias a erros sejam vericadas a cada alterao do sistema.
A camada de persistncia pode possuir muitos pontos suscetveis a falhas. Basicamente, a camada
composta por funcionalidades de escrita e leitura dos dados, que, geralmente, so implementadas com a
ajuda de APIs e arcabouos, mas o uso destas ferramentas e a criao da lgica de leitura e escrita nem
sempre so tarefas triviais.
Este captulo ir descrever alguns padres e tcnicas de automao de testes para aumentar a produ-
tividade e tornar os testes mais rpidos e robustos. Alm disso, sero discutidos as situaes tpicas de
cenrios de testes tanto para sistema de arquivos quanto para bancos de dados.
7.1 Banco de Dados
Para vericar a correo da camada de persistncia com banco de dados pode ser necessrio tanto testes
de unidade como de integrao. Basicamente, a escolha do tipo de teste depende do que se est querendo
vericar, mas a regra geral utilizar os testes de unidade sempre que possvel e fazer testes de integrao
para completar a bateria de vericaes.
Entretanto, antes de denir os tipos de testes, necessrio entender o que interessante de ser veri-
cado. A camada de persistncia de dados pode ter diversos trechos de cdigo suscetveis a erros, alm
de que os tipos de erros podem variar de acordo com os arcabouos utilizados. Por exemplo, sistemas
que possuem queries hardcoded no cdigo-fonte so muito suscetveis a erros sintticos. J para os
sistemas que utilizam arcabouos de ORM
1
, so comuns problemas de desempenho, inconsistncia de
1
Arcabouos de ORM (Object-Relational Mapping) mapeiam objetos para estrutura de banco de dados relacionais.
113
dados causados por refatoraes etc.
No caso de sistemas integrados com arcabouos ORM, algumas funcionalidades podem ser veri-
cadas com testes de unidade. Tanto as vericaes de sintaxe quanto as de lgica da aplicao podem
ser isoladas. Alm disso, alguns arcabouos disponibilizam bibliotecas especcas com Objetos Dubls
e mtodos de vericao para facilitar a criao de testes automatizados de unidade para testar o mapea-
mento dos objetos para a camada de persistncia.
J para vericar a semntica de queries, imprescindvel que os testes sejam integrados, pois elas
precisam ser interpretadas pelos gerenciadores de banco de dados. Alm disso, para testar uma query
preciso observar os efeitos colaterais causados na base de dados, ou seja, o teste verica justamente a
integrao das funcionalidades camada de persistncia.
Contudo, importante notar que existem diversos arcabouos que geram algumas queries auto-
maticamente. Estas no precisam ser vericadas, pois responsabilidade da equipe que desenvolveu o
arcabouo avaliar se elas esto sendo geradas corretamente. Assim como para todos os tipos de testes de
todas as camadas, deve-se testar apenas o sistema de interesse, dispensando as suas dependncias. Uma
situao excepcional quando h interesse em vericar o comportamento do sistema sob a ao de erros
conhecidos dos arcabouos.
No obstante, a camada de persistncia possui diversas particularidades que podem trazer dicul-
dades para automao de testes. Entre elas esto o compartilhamento de dados, sistema de conexes
de usurios, nveis e permisses de acesso, replicao da bases de dados e auditoria. No decorrer desta
seo sero discutidas algumas prticas e padres para criar testes automatizados robustos para camadas
de persistncia com banco de dados.
7.1.1 Congurao do Ambiente de Teste
Os testes de unidade da camada de persistncia so semelhantes aos de outras camadas. Sendo assim, as
dependncias devem ser substitudas sempre que possvel por Objetos Dubls para que os testes sejam
isolados. J os testes de integrao dependem da congurao e execuo correta dos ambientes e dos
gerenciadores de banco de dados necessrios. Todavia, a forma de organizao do ambiente reete
diretamente na eccia e na produtividade dos testes automatizados, por isso importante conhecer as
abordagens de congurao do ambiente.
Bancos de Dados Compartilhados
comum que empresas possuam um ou mais ambientes prprios para realizao de testes (geralmente
manuais), que so normalmente conhecidos como ambientes de desenvolvimento, de homologao ou
de garantia de qualidade. Estes ambientes so idealmente semelhantes ao de produo e teis para
a realizao de testes que integram todos os mdulos do sistema e suas dependncias. Como estes
ambientes geralmente so de alto custo, vrios departamentos e projetos acabam compartilhando os
recursos e as bases de dados.
Uma das principais vantagens desse ambiente para realizao de testes que ele prximo do
real, o que d maior credibilidade s vericaes e possibilita a criao de cenrios de testes is aos
comportamentos de usurios reais. Os testes realizados nesse ambiente tambm so teis para evitar
e identicar problemas de instalao, congurao e de portabilidade, que so aqueles que podem ser
associados frase: Mas no meu ambiente funcionava!.
Outra vantagem que a base de dados normalmente importada do ambiente real de produo, o
que permite que os testes sejam feitos com dados reais de usurios. Alm disso, o uso dos dados j
existentes pode facilitar a criao dos testes, pois dispensa parte da tarefa de preparar os dados para
execuo dos testes. A criao do set up dos testes que envolvem banco de dados exigem, via de regra,
um trabalho rduo de implementao, principalmente quando a modelagem do banco muito complexa.
114
No entanto, necessrio um esforo para pesquisa e avaliao dos dados antes que eles sejam utiliza-
dos para os testes, o que pode ser mais custoso do que a criao de novos dados. Alm disso, dicilmente
os dados pesquisados podero ser reutilizados no futuro, j que tanto os prprios testes quanto outras
pessoas podem modicar os dados.
No obstante, mesmo com as outras vantagens citadas, esse tipo de ambiente deve ser evitado para
grandes baterias de testes, sejam eles automatizados ou manuais. Ambientes compartilhados trazem in-
meras diculdades ao processo de criao, manuteno e execuo dos testes, tornando a produtividade
muito baixa.
Uma das diculdades deve-se preocupao aos dados j existentes e aos criados pelos casos de
teste. necessrio um esforo adicional para organizar e controlar os ambientes e as equipes para que
a base de dados no seja degradada com a utilizao. Mesmo assim, muito difcil manter o ambiente
estvel por muito tempo, pois muitos testes precisam criar cenrios crticos. Por exemplo, podem exigir
mudanas de congurao, gerao de dados invlidos para testes, base de dados vazia, entre outras.
Outro problema difcil de resolver a execuo concorrente de testes em um ambiente compartil-
hado, sejam eles testes de um mesmo projeto ou de projetos diferentes. Os testes no possuem o conceito
de transao, dessa forma, os comandos so executados no gerenciador de banco de dados de forma in-
determinada. Isso resulta em testes intermitentes e erros difceis de depurar. Para ilustrar, enquanto
um teste pode estar inserindo dados para vericao, outro pode estar limpando a base para preparar o
ambiente.
Ainda, a produtividade da vericao do sistema em teste tambm prejudicada devido reduo
do desempenho dos testes. A memria e o poder de processamento do ambiente so compartilhados
entre diversos usurios que podem estar realizando operaes demoradas e que exigem muitos recursos.
Alm disso, medida que os testes vo deixando resduos de dados na base, as queries de busca vo se
tornando cada vez mais lentas.
Por estas caractersticas, a relao custo/benefcio de se automatizar os testes nesse tipo de ambiente
no vantajoso. O cdigo dos testes tende a car mais extenso e complexo para que eles quem mais
robustos. Ainda assim, os testes so mais sucestveis a serem frgeis, lentos e intermitentes do que se
forem executados em outros tipos de ambientes, como veremos nas prximas subsees.
De qualquer maneira, esses ambientes so teis para garantir a qualidade dos sistemas de software.
Eles podem ser utilizados para buscar apenas erros mais especcos, a partir de testes de fumaa, de
sanidade, de instalao e de congurao. Dessa forma, apenas uma pequena parcela de todas as veri-
caes so feitas nesse ambiente, o que facilita a organizao e a manuteno dos testes e do ambiente.
Dependendo do tamanho da bateria dos testes e de outras caractersticas, pode at ser mais vantajoso
no os automatizar.
Bancos de Dados Locais
Banco de Dados Local a congurao em que os gerenciadores de banco de dados para testes so
instalados nas mquinas de cada membro da equipe de desenvolvimento. Dessa forma, cada bateria de
testes de cada membro da equipe pode ser executada paralelamente, sem grandes preocupaes, pois
os recursos do ambiente e os dados no so compartilhados. Vale notar que a base de dados ainda
compartilhada para mesma bateria de casos de testes, por isso os testes de uma mesma bateria no de-
vem ser executados paralelamente, a no ser que sejam implementados de uma maneira completamente
independente dos demais.
As vantagens e desvantagens desse tipo de congurao so praticamente opostas s de ambientes
compartilhados, o que torna as duas opes complementares para vericao da qualidade dos sistemas
de software. As conguraes de hardware e software das mquinas de desenvolvimento podem apre-
sentar grandes diferenas em relao ao ambiente de produo, no entanto, cada desenvolvedor tem total
autonomia para criao de diferentes cenrios de teste.
115
A preparao dos dados para os testes feita em mtodos de set up ou nos prprios mtodos de teste,
ou seja, h maior autonomia, controle e exibilidade sobre os dados criados. Alm disso, os registros nas
tabelas da base de dados podem ser criados de forma abstrata, com a ajuda das prprias funcionalidades
do sistema, o que facilita a criao dos cenrios de teste.
O desempenho das baterias de testes tambm pode ser signicativamente melhor com bancos de
dados locais do que com ambientes compartilhados. Apesar de as mquinas de desenvolvimento no
terem todo o potencial de processamento daquelas de servidores, elas so normalmente utilizadas por
apenas um usurio. Outro aspecto importante do desempenho deve-se menor quantidade de dados, j
que apenas umusurio de umprojeto insere dados na base. Por ltimo, bancos de dados locais dispensam
a comunicao entre mquinas, que pode at ser o principal gargalo de desempenho de muitos ambientes.
Contudo, o sucesso deste tipo de ambiente depende da congurao e organizao dos repositrios
de cdigo-fonte. Como os bancos de dados so locais, cada desenvolvedor ou testador pode possuir con-
guraes que so especcas de sua mquina, como o diretrio de instalao do banco de dados. Para
isso, existem duas solues simples: ou selecionar alguns arquivos que sero ignorados durante a sin-
cronizao com o repositrio, ou, ento, utilizar endereos de arquivos relativos e outras conguraes
dinmicas, ou seja, que no estejam hard coded.
Para nalizar, esse tipo de congurao de ambiente de teste tambm pode ser de alto custo se for
necessrio comprar licenas do gerenciador de banco de dados para cada mquina de desenvolvimento.
Contudo, este problema pode ser facilmente resolvido se as mquinas de desenvolvimento utilizarem
bancos de dados livres para realizao dos testes. Entretanto, fundamental que, em algum momento,
as baterias de testes sejam executadas sobre o gerenciadores de banco de dados utilizados em ambiente
de produo, o que pode ser feito idealmente no ambiente de integrao contnua.
Bancos de Dados em Memria
Bancos de Dados em Memria utilizam, a priori, a memria principal do computador em vez dos discos
de armazenamento. Por isso, este tipo de banco de dados intrinsecamente mais rpido para pequenas
quantidade de dados do que os tradicinais, que utilizam principalmente os discos de armazenamento.
Dessa forma, eles podem ser utilizados como banco de dados locais para o ambiente de testes.
Alm de todas as vantagens j descritas na subseo anterior, as baterias de testes podem tornar-se
signicativamente mais rpidas.
O principal obstculo para utilizar este tipo de ambiente de testes so as grandes diferenas em
relao aos bancos de dados de produo, que geralmente utilizam bancos de dados tradicionais. Por
exemplo, nem todos os bancos de dados em memria oferecem triggers e stored procedures, o que pode
impossibilitar os testes de vrios mdulos do sistema.
Mesmo assim, o recomendado utilizar os bancos de dados em memria sempre que eles permitirem
a realizao de uma grande parcela dos cenrios de testes necessrios. No entanto, fundamental que
em algum momento seja executada a bateria de testes no ambiente real de produo para evitar erros de
incompatibilidade. O mais indicado congurar o ambiente de integrao contnua [52] o mais prximo
do real, para conrmar que os resultados dos testes realizados durante o desenvolvimento so conveis.
importante observar que mesmo com banco de dados em memria ainda necessrio cuidados
para executar os testes em paralelo. Se for necessrio paralelizar os testes, uma possvel soluo criar
uma instncia distinta de banco de dados para cada caso de teste (Padro Uma Instncia de Banco de
Dados por Linha de Execuo, Seo 7.2.1).
116
7.2 Padres
Para que os testes com persistncia de dados sejam de qualidade, fundamental que a bateria como
um todo esteja muito bem organizada. Para isso, pode-se utilizar uma combinao de diversos padres
organizacionais de testes de unidade, tais como Testcase Superclass, Test Helper, Parametrized Test e
Test Utility Method [99]. Todos esses padres so teis para reutilizao de cdigo e dos dados.
No entanto, independente de como ser a organizao, fundamental que o estado do banco de dados
no incio de cada teste seja sempre o mesmo: sem dados ou apenas com um conjunto de dados essenciais
para o funcionamento do sistema. Para isso, pode-se utilizar qualquer padro que seja responsvel por
limpar todos os dados gerados por um teste, por exemplo, o Table Truncation Tear down [99]. Apenas
importante notar que invivel controlar quais foram os dados gerados por cada cenrio de teste. Por
isso, a soluo mais simples e fcil remover absolutamente todos os dados da base e recriar novamente
os essenciais. Tendo esse padro aplicado, novos padres podem ser integrados bateria de testes, como
alguns que sero descritos a seguir.
117
7.2.1 Uma Instncia de Banco de Dados por Linha de Execuo
Tipo: Desempenho
Quando utilizar: Quando uma bateria de testes se torna lenta devido grande quantidade de casos
de testes. O que deve ser levado em conta se o tempo de execuo da bateria de testes est
impactando na produtividade do desenvolvimento. O pr-requisito para que esse padro seja uti-
lizado que os testes possam ser executados em paralelo, ou seja, eles no manipulem infor-
maes compartilhadas, tais como variveis globais. Alm disso, tambm deve ser avaliado se a
infraestrutura adequada para paralelizar os testes.
Inteno: Utilizar diferentes instncias de bancos de dados para possiblitar a execuo dos testes em
paralelo, visando melhor desempenho.
Motivao: Os testes que envolvem bancos de dados tendem a possuir um desempenho inferior aos
testes de unidade devido preparao do comando a ser executado (queries) e comunicao
entre o aplicativo e o gerenciador de banco de dados. Em alguns casos, at aceitvel um teste
demorar alguns segundos para ser realizado. Por isso, o desempenho de uma grande bateria desses
testes integrados pode ser insatisfatria.
Uma boa soluo de otimizao paralelizar a execuo dos testes, no entanto, historicamente os
arcabouos de testes foram implementados para executarem os testes sequencialmente, inclusive
os que dependem de gerenciadores de banco de dados. Por isso, preciso adapt-los para que seja
possvel executar diversos testes em paralelo.
Executar testes integrados com bancos de dados paralelamente difcil porque a prpria base de
dados se torna um recurso compartilhado entre os testes, o que torna os testes dependentes entre
si. As alteraes feitas por um cenrio de teste no esquema (schema) do banco, ou mesmo nos
prprios dados, podem quebrar os demais.
Soluo: Executar a bateria de testes paralelamente em uma certa quantidade de linhas de execuo,
sendo que cada uma delas utilizar sua prpria e independente instncia do banco de dados.
Consequncias: A bateria dos testes otimizada de maneira global, o que pode dispensar micro-
otimizaes em cada cenrio de teste.
Implementao: O aplicativo que executar os testes deve ter uma linha principal de execuo que
responsvel por criar e gerenciar uma la de cenrios de testes a serem executados. Alm disso,
preciso que ela tambm inicialize e gerencie as demais linhas de execuo que iro ser executadas
em paralelo para rodar os testes automatizados. Opcionalmente, o comando para execuo da
bateria de testes pode ser parametrizado para receber a quantidade de linhas de execuo que
devero ser criadas.
Cada linha de execuo deve ser responsvel por instanciar e congurar sua prpria instncia
de banco de dados. Dado que o ambiente est congurado, a linha de execuo desempilha um
cenrio de teste para ser executado sempre que ela estiver ociosa. Quando todas as linhas de
execues estiverem ociosas e a pilha estiver vazia, a bateria de testes terminou de ser executada.
118
7.2.2 Gerao Dinmica de Dados
Tipo: Robustez
Quando utilizar: recomendado o uso em praticamente todos os testes que envolvem dados persis-
tentes em gerenciadores de bancos de dados, especialmente aqueles que precisam de muitos dados
pr-populados, tais como testes de buscas, de gerao de relatrios ou de modelos que possuem
um grande nmero de dependncias.
Inteno: Gerar automaticamente objetos tolos para facilitar a criao dos testes, assim como torn-los
mais robustos e legveis.
Motivao: Os testes que envolvem a camada de persitncia so os responsveis por criar todos os
dados necessrios para sua execuo. Esses dados podem ser classicados em duas categorias: os
principais, que caracterizam o cenrio de teste, e os tolos ((Seo 6.4.3)), que so criados apenas
para evitar erros de compilao ou de execuco, causados comumente por validaes realizadas
pelo prprio gerenciador do banco de dados.
Um problema comum ocorre quando so necessrios muitos dados tolos para realizao de um
cenrio de teste. Alm de ser uma tarefa repetitiva e tediosa, a inicializao dessas informaes
polui o cdigo de teste. Ainda, esse problema pode ser multiplicado quando um cenrio de teste
precisar de dados j existentes na base.
Para amenizar o problema de legibilidade, os dados tolos podem ser denidos de forma esttica,
geralmente em arquivos externos que sero carregados durante a preparao dos testes (Figura
7.1). Entretanto, essa no uma boa soluo. Um dos principais problemas dessa abordagem
que a quantidade de dados para os testes cresce rapidamente medida que novos casos de testes
so adicionados, o que torna a manuteno dos testes muito complicada, pois qualquer mudana
na modelagem pode implicar que todos os arquivos de testes precisem ser adaptados.
1 - model: Pessoa
2 id: 1
3 nome: Sharon Janny den Adel
4 sexo: F
5 data_nascimento: 1974/07/12
6 nacionalidade: Holandesa
7
8 - model: Pessoa
9 id: 2
10 nome: Mark Knopfler
11 sexo: M
12 data_nascimento: 1949/08/12
13 nacionalidade: Escocesa
Figura 7.1: Exemplo de dados estticos em um arquivo no formato YAML.
Uma possvel soluo para esse problema tentar reaproveitar os dados para diferentes cenrios
de teste, o que diminui a replicao de informaes e facilita a manuteno em caso de mudanas
na modelagem dos dados. Entretanto, essa abordagem possui problemas ainda maiores. Com-
partilhar informaes entre testes os tornam frgeis, de modo que qualquer pequena alterao na
bateria de testes ou no prprio sistema pode quebrar um grande conjunto de testes, o que diculta
a identicao do erro e, consequentemente, torna a manuteno muito complicada (Seo 7.1.1).
Para dicultar ainda mais, essa abordagem prejudica a clareza dos testes, pois no ca expltico
quais os dados que so realmente pertinentes para cada caso de teste.
119
Soluo: Criar mtodos que geram dinamicamente objetos de dados populados com dados tolos e vli-
dos. Tendo esse objeto instanciado, basta atualizar as informaes principais com valores perti-
nentes para simular o cenrio de teste desejado.
Consequncias: Primeiramente, no mais necessrio a criao e manuteno de arquivos estticos de
dados, muito menos o gerenciamento de dados compartilhados. Alm disso, os testes so mais
facilmente adaptados s mudanas da modelagem dos dados. Por m, a clareza e a legibilidade
dos testes so melhoradas, no s porque os testes tendem a car mais enxutos e coesos, como
tambm porque os dados tolos de um objeto podem ser escondidos.
Implementao: No escopo dos testes, deve-se criar uma biblioteca de funcionalidades que so re-
sponsveis por instanciar e persistir objetos de dados populados com objetos tolos. Contudo, os
objetos tolos precisam ser nicos, para evitar problemas de integridade de dados, e no aleatrios,
para facilitar a depurao de testes incorretos e para no tornar os testes intermitentes. Tambm
importante notar que, como um objeto de dado pode depender de outros, essa biblioteca pode
fazer chamadas internas para reaproveitamento de cdigo.
J para tornar essa biblioteca mais exvel, pode-se permitir que um cenrio de teste consiga per-
sonalizar as informaes que forem pertinentes para seu contexto, ou seja, os mtodos de gerao
de objetos devem ser parametrizveis. Entretanto, o trabalho de implementao dessa biblioteca
de objetos de dados semelhante para todos os objetos de dados, logo, altamente recomendvel
que seja utilizado reexo para criao de bibliotecas genricas, ou seja, que consigam criar di-
namicamente qualquer tipo de objeto de dados.
Exemplo Python/Unittest: Para testar uma funcionalidade de busca de pessoas, muitos objetos de da-
dos Pessoa precisaro ser criados para poder simular todos os cenrios desejados. Por isso, antes
mesmo da criao dos testes, importante criar classes para gerar dinamicamente os objetos de
dados desejados. Esse objeto ter a responsabilidade de criar um objeto vlido, com pelo menos os
atributos obrigatrios do objeto preenchidos, mas de modo que possa ser customizado de acordo
com a necessidade dos testes. A Figura 7.2 mostra um exemplo de implementao desse objeto
(GeradorDePessoas) e de um teste que o utiliza.
Exemplo Python/Unittest/Django Dynamic Fixture: Como foi citado nos tpicos anteriores, a im-
plementao dessa classe de gerao de dados um trabalho rduo e repetitivo . Por isso, pref-
erencialmente, recomendado utilizar bibliotecas genricas de gerao dinmica de dados, como
o caso da Django Dynamic Fixture, para sistemas Web que utilizam o arcabouo Django para
Python. A Figura 7.3 mostra alguns exemplos de teste para a mesma funcionalidade de busca de
pessoas.
Usos Conhecidos: A ferramenta Django Dynamic Fixture implementa esse padro para aplicaes que
utilizam o arcabouo Django para Python.
120
1 from cadastro.models import Pessoa
2
3 import unittest
4
5 class GeradorDePessoas(object):
6 def cria(nome=X, sexo=F, nascimento=2000/01/01):
7 p = Pessoa(nome=nome, sexo=sexo, nascimento=nascimento)
8 p.save()
9 return p
10
11 gerador = GeradorDePessoas()
12
13 class BuscaDePessoasPeloNomeTest(unittest.TestCase):
14 def test_deve_encontrar_pessoas_que_possuem_parte_do_nome_buscado(self):
15 pessoa_que_deve_ser_encontrada = gerador.cria(nome=Sharon Janny den Adel)
16 pessoa_que_nao_deve_ser_encontrada = gerador.cria(nome=Mark Knopfler)
17
18 pessoas_encontradas = Pessoa.objects.com_parte_do_nome(Jan)
19
20 self.assertTrue(pessoa_que_deve_ser_encontrada in pessoas_encontradas)
21 self.assertTrue(pessoa_que_nao_deve_ser_encontrada not in
pessoas_encontradas)
Figura 7.2: Exemplo em Python de classe de gerao dinmica de um objeto de dados especco.
121
1 from django_dynamic_fixture import get
2 from cadastro.models import Pessoa
3
4 import unittest
5
6 class BuscaDePessoasPeloNomeTest(unittest.TestCase):
7 def test_deve_encontrar_pessoas_que_possuem_parte_do_nome_buscado(self):
8 # Apenas o atributo nome pertinente para esse caso de teste.
9 # Portanto, os outros atributos do objeto Pessoa devem ficar ocultos.
10
11 # cria e salva na base de dados uma pessoa com um nome especfico.
12 pessoa_que_deve_ser_encontrada = get(Pessoa, nome=Sharon Janny den Adel)
13 pessoa_que_nao_deve_ser_encontrada = get(Pessoa, nome=Mark Knopfler)
14
15 pessoas_encontradas = Pessoa.objects.com_parte_do_nome(Jan)
16
17 self.assertTrue(pessoa_que_deve_ser_encontrada in pessoas_encontradas)
18 self.assertTrue(pessoa_que_nao_deve_ser_encontrada not in
pessoas_encontradas)
19
20 def test_deve_ignorar_se_texto_buscado_esta_em_minuscula_ou_maiuscula(self):
21 pessoa_que_deve_ser_encontrada = get(Pessoa, nome=Kyra Gracie Guimares)
22 pessoa_que_nao_deve_ser_encontrada = get(Pessoa, nome=Roger Gracie Gomes)
23
24 pessoas_encontradas = Pessoa.objects.com_parte_do_nome(guiMARes)
25
26 self.assertTrue(pessoa_que_deve_ser_encontrada in pessoas_encontradas)
27 self.assertTrue(pessoa_que_nao_deve_ser_encontrada not in
pessoas_encontradas)
28
29 class BuscaDePessoasPorSexoTest(unittest.TestCase):
30 def test_deve_encontrar_apenas_pessoas_do_sexo_buscado(self):
31 # Dessa vez, o nome no importante, pode-se utilizar um nome tolo
qualquer,
32 # que criado pela biblioteca automaticamente.
33 pessoa_que_deve_ser_encontrada = get(Pessoa, sexo=F)
34 pessoa_que_nao_deve_ser_encontrada = get(Pessoa, sexo=M)
35
36 pessoas_encontradas = Pessoa.objects.de_sexo(F)
37
38 self.assertTrue(pessoa_que_deve_ser_encontrada in pessoas_encontradas)
39 self.assertTrue(pessoa_que_nao_deve_ser_encontrada not in
pessoas_encontradas)
Figura 7.3: Exemplo do padro de Gerao Dinmica de Dados com a biblioteca genrica de objetos de
dados Django Dynamic Fixture.
122
Captulo 8
Testes de Interface de Usurio
Interface de usurio (IU) a camada que cria a ligao entre o usurio e as camadas internas do sistema.
Os tipos mais comuns de IUs so as interfaces grcas (GUI, de Graphic User Interface) e as interfaces
Web (WUI, de Web User Interface). Os componentes das IUs observam eventos originrios de dispos-
itivos de hardware, identicam os eventos e fazem chamadas a outros mdulos do sistema por meio de
troca de mensagens ou de chamada de mtodos.
As IUs so fundamentais para muitos projetos e podem ser a camada determinante para o sucesso
de um sistema. Elas podem tornar o uso do sistema intuitivo, o que possibilita que mesmo pessoas
inexperientes consigam aprender a utiliz-lo sem a necessidade de um estudo rduo de documentos.
Atrativos que tornem o uso da interface mais fcil e interessante so diferenciais que valorizam muito o
sistema como um todo, vide as interfaces multi touch que tm atrado a ateno de milhes de usurios
em todo o mundo [103].
Devido importncia das IUs, interessante que seja dedicada uma parcela considervel de esforo
para que ela seja bem implementada. A qualidade das IUs pode ser interpretada pelas caractersticas
de correo, usabilidade, acessibilidade, portabilidade e beleza. Alm disso, estes fatores so, muitas
vezes, determinantes para a aceitao do sistema por parte do cliente [139].
No entanto, controlar e garantir a qualidade dessa camada do sistema uma tarefa delicada. A
interface de usurio ca exposta ao manuseio dos seres humanos, que possuem comportamentos inde-
terminados. Portanto, esta camada est sujeita a lidar com uma innidade de combinaes inusitadas de
eventos. Alm disso, muitos sistemas esto sujeitos a alteraes frequentes da interface, principalmente
os sistemas Web que evoluem constantemente seu leiaute [73].
Essas particularidades das IUs requerem que o controle de qualidade seja gil (de fcil adaptao),
ecaz e seguro contra erros de regresso. Mtodos formais so geralmente inviveis devido s mu-
danas constantes que a interface est sujeita e tambm por causa do pouco embasamento matemtico
do cdigo-fonte, com exceo de componentes multi touch que precisam interpretar traos e guras
geomtricas. J os testes manuais so ecazes para certos aspectos de qualidade, como beleza e usabili-
dade, mas no so seguros contra erros de regresso, principalmente por causa da innidade de maneiras
que uma interface pode ser manipulada.
A automao de testes uma alternativa satisfatria para o controle de qualidade das IUs, por todos
os motivos que j foram discutidos na Parte I. Contudo, para que os testes automatizados para esta
camada sejam de qualidade, necessrio o uso de ferramentas especializadas, que permitam a escrita de
testes legveis com alta produtividade.
No decorrer deste captulo sero discutidos alguns aspectos de ferramentas que so teis para criao
de testes automatizados de interface com qualidade. Tambm sero apresentadas abordagens prprias
para criao de testes de interface, assim como padres e antipadres que so especcos para este tipo
de teste.
123
8.1 Princpios Bsicos
Como muitas das responsabilidades de um sistema so associadas interface de usurio, mais difcil
test-la do que mdulos isolados que so responsveis por apenas uma tarefa. Por mais que o sistema e a
interface sejam bem modularizados, os problemas de outras partes do sistema podem acabar interferindo
na interface grca.
Por isso, fazer testes automatizados de interface de qualidade requer no s conhecimento de padres
e antipadres de testes, como tambm muita disciplina. Para organizar bem os testes necessrio con-
hecer primeiramente as responsabilidades da interface de usurio, assim como modularizar o sistema
corretamente para facilitar a escrita dos mesmos.
Um projeto mal arquitetado no separa as regras de negcio e de dados do cdigo da interface de
usurio. Por exemplo, comandos SQL integrados comcdigo-fonte responsvel por desenhar a interface.
Este problema de arquitetura comum, por isso, hoje em dia, muitos arcabouos possuem uma estrutura
denida para evitar que isso acontea, principalmente os arcabouos para desenvolvimento de sistemas
Web.
Os arcabouos para criao de interfaces grcas seguem, em geral, a arquitetura baseada no padro
MVC (Model-View-Controller) [32]. Alguns seguem variaes deste padro, mas sempre incentivando
a separao do sistema em mdulos bem denidos, o que torna o software mais fcil de implementar e
testar. O padro MVC sugere uma arquitetura na qual o sistema dividido em trs partes: (1) Modelo
responsvel por encapsular as regras e os dados do sistema; (2) Viso, que exibe os dados e (3) Contro-
lador, que recebe informaes do usurio e as envia para camada de Modelo. O Controlador recebe da
Viso as aes do usurio e as mapeia para as funcionalidades pertinentes ao Modelo. A partir dos resul-
tados, o Controlador manuseia a Viso apropriadamente. A Figura 8.1 possui um diagrama simplicado
mostrando as associaes entre as camadas.
Figura 8.1: Diagrama simplicado do padro MVC. As linhas slidas indicam associaes diretas en-
quanto as tracejadas representam associaes indiretas.
Dado que a aplicao segue a arquitetura MVC, pode-se dizer que a interface grca est associada
Viso, apesar de nem toda Viso ser uma interface grca. O termo Viso mais amplo, pois abrange
tudo que tenha o propsito de exibir os dados. Por exemplo, Viso pode ser imagens, grcos, tabelas,
sons etc. Entretanto, os testes de interface tambm envolvem, comumente, a camada Controlador, pois
ela interfere diretamente nas informaes que sero exibidas, alm de que o cdigo-fonte do controlador
pode estar entrelaado com o cdigo dos arcabouos que facilitam a criao da Viso.
Controlador
A responsabilidade do Controlador basicamente conectar duas outras partes do sistema, pois ela recebe
os dados do usurio a partir da Viso e as envia apropriadamente para camada Modelo. Por isso a sua
lgica no deve ter grande complexidade. Entretanto, erros do controlador podem ser to desastrosos
124
quanto erros em mdulos mais complexos. Portanto, automatizar os testes dos controladores tambm
importante.
Os controladores podem ser avaliados por meio de efeitos colaterais causados pelo manuseio da
interface de usurio. Como sua execuo possivelmente altera o estado da interface, possvel seguir
essa estratgia. Essa forma de testar especialmente til nas situaes em que o cdigo do controlador
difcil de ser isolado e testado. Sistemas legados mal implementados ou os que usam arcabouos
complexos tornam invivel a criao de testes de unidade de qualidade. Estes testes integrados com a
camada de interface do usurio se tornam uma alternativa produtiva e segura, at que os controladores
sejam refatorados.
Contudo, sempre que possvel e vivel, prefervel testar isoladamente os controladores com testes
de unidade, assim como qualquer outro componente do sistema. Testes de unidade so mais fceis e
rpidos de serem escritos e mantidos do que testes que integram diversos mdulos do sistema.
Os controladores da interface no possuem tantas particularidades quanto Viso, j que ela no
acessvel ao usurio nal. Entretanto, ela possui duas caractersticas expressivas que podem afetar
sua testabilidade: (1) comum que a lgica da camada que entrelaada com trechos de cdigo para
satisfazer as necessidades de arcabouos e (2) ela comumente possui referncias para diversos outros
mdulos do sistema.
No caso de aplicaes Web, o controlador est sujeito a lidar com detalhes do protocolo HTTP.
Ao ser chamado, ele pode ter de processar as informaes que vieram por meio de requisies GET
ou POST. Para o retorno, ele pode lanar os erros 403 (funciolidade proibida) e 404 (pgina no en-
contrada) ou, at mesmo, trechos de cdigo HTML, JavaScript, JSON e XML para serem exibidos
apropriadamente na Viso.
O mesmo ocorre para o caso dos controladores de interfaces grcas, os quais so mais fceis de
ser implementados com a ajuda de arcabouos. O controlador obtm as informaes vindas do usurio
atravs de objetos ou estruturas de dados prprias. Sua execuo tambm pode ter de seguir a forma
que os arcabouos so organizados para que a interface seja implementada corretamente. Por exemplo,
programas escritos com o arcabouo Swing para linguagem Java precisam se preocupar com o modo que
iro fazer as chamadas para as camadas base do sistema, pois a execuo lenta das tarefas pode impedir
que o processo de renderizao da interface seja executado, bloqueando a interface.
A primeira recomendao para realizao de bons testes de unidade dos controladores utilizar
o padro Humble Object (Seo 6.4) sempre que for pertinente. Dessa forma, o teste ca direcionado
totalmente lgica do sistema enquanto a lgica do arcabouo de apoio ignorada. Erros dos arcabouos
podem afetar o sistema, mas sempre supomos que ele funciona como o esperado. Se o arcabouo
possuir um erro j conhecido, ento pode ser implementado um teste para vericar se o sistema consegue
administr-lo apropriadamente.
Atualmente, diversos arcabouos fornecem no apenas seus arquivos compilados como, tambm,
bibliotecas para facilitar a escrita de testes automatizados dos mdulos que utilizem suas funcional-
idades. Isso facilita e aumenta a produtividade da criao de testes. o caso dos arcabouos Web
Django e Ruby on Rails que fornecem bibliotecas que permitem simular requisies de usurios e que
possuem mtodos de vericaes que tornam os testes mais legveis e fceis de serem escritos.
Outra recomendao implementar os controladores de um modo que seja possvel injetar as de-
pendncias [113], isto , que o teste consiga substituir os objetos da camada de negcios por Objetos
Dubls. Dessa forma, a persistncia de dados e a comunicao entre servidores podem ser facilmente
isoladas.
Viso
Teste de interface verica a correo da interface do usurio. Outros tipos de testes tambm avaliam a
interface, mas utilizando outros critrios, como, por exemplo, os testes de usabilidade, acessibilidade,
125
leiaute e aceitao. Portanto, as ferramentas de testes de interface podem ser utilizadas para a realizao
desses outros tipos de testes. No entanto, como foi discutido no Captulo 4, importante focar no que se
est querendo vericar, assim como imprescindvel no misturar baterias de testes com objetivos dis-
tintos. possvel separar as baterias de testes que envolvem a interface sem a necessidade de replicao
de cdigo-fonte, como ser discutido na seo de padres deste captulo.
Existem duas abordagens para realizao dos testes que envolvem a interface de usurio: (1) testar a
implementao da interface, vericando o uso das bibliotecas e arcabouos de MVC e de renderizao;
ou (2) fazer os testes diretamente na interface a partir da simulao de um usurio nal. Essas duas abor-
dagens podem ser simplicadamente ditas como testes de caixa-branca e caixa-preta, respectivamente.
Na prtica, as duas abordagens podem ser implementadas com um certo conhecimento do cdigo-fonte,
ou seja, o termo mais apropriado seria caixa-cinza em vez de caixa-preta.
A abordagem de caixa-branca nada mais do que fazer testes de unidade para a Viso. Para isso,
necessrio a criao dos eventos que representam as aes dos usurios e importante isolar a interface
dos controladores. A interface tambm pode ter mtodos auxiliares que encapsulam parte de sua lgica.
Esses mtodos tambm devem ser testados, como qualquer outra regra do sistema. Para esse tipo de
teste, podem ser utilizadas as ferramentas normais de testes de unidade, como as da famlia xUnit, assim
como as ferramentas de criao de Objetos Dubls (vide Seo 6.2).
No caso das interfaces Web, mais complexo fazer os testes de unidade. A interface implementada
usando linguagem de marcao (HTML) e outras que so interpretadas pelo navegador Web, como
JavaScript. O arcabouo JsUnit, que a verso JavaScript da famlia xUnit, utiliza o prprio navegador
para executar os testes e fornece um servidor Web leve para tornar possvel a execuo automtica, por
exemplo, em servidores de integrao contnua, como descrito na Seo 4.5.
Alm disso, as aplicaes Web so distribudas: a interface do usurio ca localizada no lado do
cliente, enquanto os controladores e o modelo cam no lado do servidor. Isso impossibilita que os
controladores ou outros mdulos do sistema sejam facilmente substitudos por Objetos Dubls. Essa
diculdade um tpico interessante para pesquisas futuras. Outra diculdade ainda no solucionada
medir a cobertura dos testes em relao ao cdigo (vide Captulo 10) de interfaces Web.
Existem ferramentas que fazem chamadas ao sistema pela interface simulando um usurio, mas o
teste passa a ser integrado, pois envolve tambm o lado do servidor. o caso das ferramentas HtmlU-
nit
1
, JWebUnit
2
e Selenium
3
. Elas simulam os usurios nais e coletam informaes da interface para
vericao. Alguns detalhes da implementao so utilizados, tais como propriedades de componentes,
como, por exemplo, nomes dos componentes, identicadores etc. No entanto, grande parte dos detalhes
de implementao da interface e do controlador so abstrados pelas ferramentas.
Tambm existem ferramentas anlogas para fazer testes em interfaces Desktop. Assim como as
ferramentas para testes Web podem depender do navegador, as de interfaces Desktop podem depender do
gerenciador de janelas do sistema operacional, da linguagem de programao e das bibliotecas utilizadas
para implementar a interface do sistema. Por exemplo, as ferramentas Fest e Jemmy so especcas para
testes de interfaces grcas implementadas em Java Swing.
Essas ferramentas so as mesmas utilizadas em um teste caixa-preta. Entretanto, mais importante
do que o conhecimento dos testes em relao ao cdigo-fonte, se os testes iro substituir mdulos
do sistemas por Objetos Dubls para tornar o teste menos acoplado. Essa deciso depender dos tipos
de erros que sero vericados, assim como da facilidade de implementao dos testes e do uso das
ferramentas.
1
Apesar de o nome sugerir que a ferramenta da famlia xUnit, HtmlUnit um navegador leve escrito em Java que no
renderiza as pginas, mas consegue processar o cdigo HTML e interpretar o cdigo JavaScript. Ele utilizado para testes de
integrao e serve como base para muitas ferramentas, tanto para testes em Java quanto para outras linguagens, como Python
e Ruby.
2
JWebUnit um Wrapper do HtmlUnit para facilitar a escrita dos testes e torn-los mais claros.
3
Selenium uma ferramenta escrita em JavaScript que executada internamente em navegadores reais. Atualmente, est
sendo unicada com a ferramenta WebDriver, que possui o mesmo propsito da JWebUnit.
126
8.2 Testes que Simulam Usurios
Os testes que simulam aes dos usurios possuem peculiaridades importantes. O modo pelo qual o
sistema exercitado no por intermdio de chamadas diretas a objetos e mdulos do sistema, mas,
sim, por arcabouos que manipulam a prpria interface de forma transparente, sem a necessidade de
conhecer a fundo o cdigo-fonte. J a forma de vericao pode variar dependendo do que se est
querendo vericar. Podem ser feitas vericaes diretamente no estado da interface, ou, ainda, em
outras camadas do sistema, como na camada de persistncia de dados.
A lgica dos testes de interface segue sempre uma estrutura denida, que ilustrada pelo exemplo
de uso da ferramenta HtmlUnit na Figura 8.2. Primeiro preciso localizar um componente da pgina
(linhas 17, 19 e 23) para, em seguida, simular algum evento do usurio nal (linhas 21 e 26) ou capturar
propriedades para fazer as vericaes (linha 28). Os eventos mais comuns so cliques do mouse e teclas
digitadas. Entretanto, existem ferramentas que tambm disponibilizam mtodos para simular eventos de
arrastar e soltar (drag and drop), apertar e segurar teclas etc.
1 // referncias do JUnit
2 import org.junit.Test;
3 import static org.junit.Assert.assertEquals;
4 // referncias do HTMLUnit
5 import com.gargoylesoftware.htmlunit.WebClient;
6 import com.gargoylesoftware.htmlunit.html.*;
7
8 public class BuscaTests {
9
10 @Test
11 public void buscaComSucessoDeveRedirecionarParaPaginaDeResultados() {
12 // Cria um cliente (navegador)
13 WebClient cliente = new WebClient();
14 // Abre uma pgina armazenada em um servidor local
15 HtmlPage pagina = cliente.getPage("http://localhost:8000");
16 // Localiza o form de busca
17 HtmlForm formulario = pagina.getFormByName("busca_form");
18 // Localiza o campo de texto para busca do formulrio
19 HtmlTextInput texto_busca = formulario.getInputByName("texto_busca");
20 // Digita o texto para busca
21 texto_busca.setValueAttribute("tdd");
22 // Localiza o componente para submeter o form
23 HtmlSubmitInput botao_busca = formulario.getInputByName("busca");
24 // Simula um clique no boto de envio de dados para o servidor
25 // Abre a pgina de resultados
26 HtmlPage pagina_resultados = botao_busca.click();
27 // Verifica se a ao foi executada corretamente
28 assertEquals("Resultados da Busca - tdd", page.getTitleText());
29 }
30 }
Figura 8.2: Exemplo de teste de interface Web com HtmlUnit.
As vericaes deste exemplo so feitas com auxlio da ferramenta JUnit. Como a HtmlUnit ape-
nas cria um navegador para execuo dos testes, interessante a utilizao de outras ferramentas que
facilitem as vericaes. Outras ferramentas tambm fornecem mtodos de vericaes que so mais
pertinentes para testes de interface do que as da famlia xUnit, como o caso da JWebUnit e da Sele-
nium. Esses mtodos podem ser desde aucares sintticos que melhoram a legibilidade dos testes at
mtodos fundamentais, por aumentar consideravelmente a produtividade da automao. Alm disso,
127
algumas ferramentas possuem gravadores de interao, discutidos a seguir.
8.2.1 Gravadores de Interao
Gravadores de Interao so ferramentas que detectam aes executadas por usurios reais sobre um
programa e geram cdigo-fonte ou metadados que podem ser interpretados e reproduzidos para simular
um usurio. Alm disso, elas permitem adicionar pontos de vericao manualmente durante ou depois
da gravao por meio da edio do cdigo-fonte gerado. Exemplos de ferramentas so Marathon
4
, para
testes de GUIs geradas com Java/Swing, e Selenium-IDE
5
, para WUIs.
A principal vantagem da gravao de testes a facilidade de sua escrita, j que no necessrio con-
hecimento de programao. Assim, qualquer pessoa pode criar casos de testes automatizados, mesmo
clientes e usurios nais. A produtividade tambm pode ser alta, j que o trabalho de localizao dos
componentes da tela transparente para o usurio.
Contudo, o cdigo gerado pode no ser muito legvel e modularizado, o que resulta na replicao
de cdigo e difcil manuteno. Qualquer alterao na interface de usurio pode afetar diversos casos
de testes. Se muitos testes precisarem ser arrumados ou at mesmo refeitos ou regravados do zero, a
produtividade dessa abordagem pode diminuir.
A qualidade dos testes automatizados de interface depende das ferramentas utilizadas, por isso tam-
bm interessante conhecer as limitaes das ferramentas. Existem casos onde nem todos os eventos de
usurios podem ser gravados, sendo necessria a edio do cdigo-fonte para complementar o teste.
Por essas complicaes, a abordagem recomendada para evitar esses problemas refatorar o cdigo-
fonte gerado aps a gravao da simulao de forma a organizar o cdigo-fonte gerado. Utilizar a
gravao pode aumentar a produtividade durante a criao dos testes, assim como mais produtivo dar
manuteno em cdigo-fonte organizado e modularizado.
No entanto, a gravao da simulao s faz sentido aps a implementao da interface, portanto
esta abordagem conitante com as tcnicas de escrever os testes antes mesmo da implementao
(vide Captulo 9). Entretanto, nada impede que as duas abordagens sejam mescladas, dependendo das
situaes.
J para os sistemas legados, em que o cdigo-fonte est muito embaralhado ou incompreensvel, a
gravao de testes uma tcnica rpida e interessante para trazer segurana nas futuras manutenes do
sistema
6
. Alm disso, ela pode ser til para criao de outros tipos de testes, como os de usabilidade,
acessibilidade e aceitao.
Outro uso interessante dos gravadores a criao de testes de fumaa bem simples e sem a neces-
sidade de refatorao do cdigo-fonte gerado. Como a criao dos testes rpida e os cenrios so
simples, o custo de refaz-los do princpio tende a ser menor do que o custo de refatorar o cdigo ger-
ado. Testes de fumaa so, muitas vezes, utilizados em ambientes de produo, desde que no sejam
realizadas aes crticas que possam comprometer o sistema e os dados em caso de erro.
8.3 Desempenho dos Testes
Os testes de interface so intrinsecamente mais lentos do que aqueles das outras camadas do sistema. Por
mais que eles sejam isolados, podem envolver processos de renderizao e de manuseio de dispositivos
de hardware para criao dos eventos de usurio. Para os sistemas Web, a situao ainda mais delicada,
4
Marathon uma ferramenta implementada em Python que permite gravar testes para aplicaes escritas em Java/Swing.
5
Selenium-IDE uma extenso para o navegador Firefox que permite gravar, editar e executar testes escritos com a ferra-
menta Selenium
6
O autor deste trabalho aplicou esta recomendao em um sistema Web durante seu estgio na Assembleia Legislativa do
Estado de So Paulo.
128
pois pode ser necessrio o apoio de navegadores Web e de servidores para integrar o lado do cliente e o
do servidor.
No caso dos testes de integrao, o desempenho pode baixar a nveis crticos. A persistncia de
dados no sistema de arquivos, ou em banco de dados, tambm uma operao lenta, podendo at ser um
dos gargalos de desempenho, assim como a comunicao lenta entre redes e servidores
7
.
O ideal da automao de testes que todos os cenrios possam ser executados a todo momento e
praticamente instantaneamente. No caso dos testes de interface, pode haver uma certa tolerncia quanto
ao desempenho, desde que eles no sejam to lentos a ponto de atrasar o desenvolvimento do projeto.
O mesmo ocorre com outros tipos de testes intrinsecamente demorados, tais como os de aceitao. No
caso de grandes baterias de testes de WUI, elas podem demorar at horas para serem executadas.
Existem algumas ferramentas que podem acelerar a execuo dos testes ou recomendaes para
tornar o uso da automao mais prtica. A ferramenta Selenium Grid, por exemplo, cria uma grade
computacional para possibilitar que os testes escritos com a ferramenta Selenium sejam executados par-
alelamente em diversos ambientes. Essa ferramenta especialmente til quando uma mesma bateria
de testes executada em vrios navegadores Web para testar a portabilidade da aplicao. Contudo,
para que ela seja utilizada com xito, fundamental que os casos de testes sejam completamente in-
dependentes uns dos outros e da ordem em que so executados. Como mais de um caso de teste pode
ser executado ao mesmo tempo, um poder inuenciar o sucesso de outro, caso haja algum recurso
compartilhado.
Os erros causados pela dependncia dos testes inviabilizam a execuo paralela da bateria de testes.
A quantidade de erros que podem surgir indeterminada alm de que os erros possivelmente sero
intermitentes. Mesmo os relatrios dos resultados dos testes no sero mais teis porque a bateria de
testes perde a sua propriedade de reprodutibilidade.
Outra recomendao utilizar as ferramentas com melhor desempenho durante o perodo de desen-
volvimento e deixar as mais lentas para serem executadas em ambientes de integrao contnua, pelo
menos uma vez ao dia. Para interfaces Web, existem ferramentas que permitem executar a mesma bate-
ria de testes em diferentes navegadores. Dessa forma, durante o dia a dia de desenvolvimento, os testes
podem ser executados no navegador do HtmlUnit, que leve e tem melhor desempenho porque no
renderiza a interface. J no ambiente de integrao contnua, os testes podem ser executados de forma
assncrona nos navegadores reais, como Firefox e Google Chrome.
7
O autor teve essa experincia enquanto trabalhava com o sistema Janus, da Pr-Reitoria de Ps-Graduao da USP. O
sistema possuia uma bateria de testes de integrao e uma de testes de fumaa para a sua interface Web. Os testes de integrao
eram executados em um ambiente de integrao contnua, que era um servidor Linux com uma sesso grca virtual
129
8.4 Padres
A seguir sero descritos padres de automao para testes que envolvem a interface de usurio. Estes
padres foram identicados pelo autor em projetos prprios e com o estudo de certas ferramentas de
testes que j implementam algumas das solues propostas. Os padres sero descritos segundo o es-
queleto exibido na Seo 5.3. Quando houver diferena signicativa na implementao do padro para
interfaces GUI e WUI, haver subtpicos prprios para descrever cada caso.
8.4.1 Tela como Unidade
Tipo: Organizacional
Quando utilizar: Idealmente, em todos os testes de interface de usurio.
Inteno: Facilitar a implementao dos mtodos de set up, tornar os testes menos sensveis a mudanas
do sistema e melhorar o desempenho deles.
Motivao: Para o teste chegar at a tela a ser testada pode ser necessria a navegao por meio de
diversas outras telas do sistema, o que acaba adicionando responsabilidades adicionais ao teste
desnecessariamente. Esta navegao normalmente deve car armazenada no mtodo de set up,
pois uma preparao do ambiente para realizao dos testes.
A navegao torna a criao do teste mais complexa e gera uma dependncia de outras telas do
sistema. Alm disso, o carregamento de outras telas pode armazenar na memria informaes que
no esto explcitas na descrio do teste, o que, se ocorrer, diculta o entendimento dos casos de
teste. Por ltimo, este processo exige que outras telas que no so pertinentes para os testes sejam
carregadas e renderizadas, o que demanda tempo.
Soluo: Abrir a tela diretamente, dispensando a necessidade de navegar por outras telas do sistema.
Consequncias: A navegao entre telas evitada, o que torna o caso de teste mais rpido e indepen-
dente de outras partes do sistema, assim como esperado com os testes de unidade.
Implementao - GUI: Deve-se instanciar os objetos que representam a tela diretamente. Se no for
possvel abrir a tela dessa maneira, pode-se adaptar o cdigo do sistema, ou, ento, o objeto pode
ser encapsulado no mtodo de set up em um outro objeto que permita a abertura da tela. Algumas
ferramentas de testes fornecem suporte para isso.
Implementao - WUI: A pgina deve ser aberta diretamente atravs da URL, por mais que o endereo
seja longo. S necessrio cautela para que o domnio da aplicao esteja denido em apenas um
local da aplicao, o que evita a replicao de cdigo e facilita a manuteno dos casos de teste.
Exemplo - GUI - Java/Swing/JUnit/Fest: Temos uma tela de conguraes em teste, exibida na
Figura 8.3 (fotograa de uma tela do sistema GinLab). O primeiro passo abrir a tela para que
ela seja manipulada pelos testes. Essa tela aberta da mesma forma que um usurio do sistema,
utilizando links ou atalhos de teclado presentes em outra tela do sistema, como mostrado na
Figura 8.4.
Neste caso, o set up dos testes precisa descrever todos os passos que o usurio faria, como
mostrado na Figura 8.5. Note que necessrio conhecer alguns detalhes de implementao e do
funcionamento da tela principal (representada pelo objeto TelaPrincipalFrame) do sistema para
poder fazer os testes da tela de congurao. Isso torna o teste menos robusto, mais complexo,
lento e ilegvel.
130
Figura 8.3: Tela de conguraes a ser testada.
Figura 8.4: Tela principal do sistema que contm links e atalhos de teclado para abrir a tela de congu-
raes a ser testada.
Contudo, possvel quebrar esta dependncia se conseguirmos instanciar isoladamente a janela
que ser testada (representada pelo objeto TelaConfiguracoesFrame), como apresentado na
Figura 8.6. pertinente notar que os testes do menu e dos atalhos so importantes de serem
realizados, mas eles devem estar agrupados com os testes da janela que os contm, ou seja, com
os testes da tela principal.
Exemplo em Java - WUI/WebDriver: Para testar sistemas Web, podemos acessar sua pgina inicial e
ento navegar at o mdulo desejado. A Figura 8.7 segue esta estratgia para acessar uma pgina
de autenticao da aplicao. Note que este um exemplo simples, onde necessrio passar por
apenas uma pgina para encontrar um link para o local que queremos testar. Em outras situaes,
pode ser necessrio navegar por diversas outras pginas, deixando o cdigo dos testes ainda mais
complexo e lento.
Para sistemas Web, mais simples acessar as pginas diretamente, j que, normalmente, o en-
dereo completo da pgina desejada pode ser aberto diretamente, como mostrado na Figura
8.8. Quando no for possvel acessar o mdulo para teste diretamente, deve ser utilizado o en-
dereo da pgina mais prxima para que se possa navegar at o local desejado. Isso, geralmente,
necessrio quando for preciso carregar algumas informaes por intermdio da submisso de
formulrios Web antes de acessar a pgina que ser testada.
Padres Relacionados: O padro Estado Inicial da Tela (Seo 8.4.2) facilita a implementao desse
padro.
Usos Conhecidos: Os testes dos sistemas Janus e GinLab seguem essa organizao.
131
1 // junit
2 import org.junit.Before;
3 // FEST
4 import org.fest.swing.fixture.FrameFixture;
5 // Cdigo do pacote AWT utilizado pelo FEST
6 import java.awt.event.KeyEvent;
7
8 public class UmTest {
9 private FrameFixture window;
10
11 @Before
12 public void abreTelaEmTeste() {
13 window = new FrameFixture(new TelaPrincipalFrame());
14 window.show();
15 window.focus();
16 window.menuItem("arquivo").click();
17 window.menuItem("configurar").click();
18
19 // ou poderia abrir a janela por atalhos de teclado:
20 // window.pressKey(KeyEvent.VK_CONTROL);
21 // window.pressKey(KeyEvent.VK_C);
22 // window.releaseKey(KeyEvent.VK_C);
23 // window.releaseKey(KeyEvent.VK_CONTROL);
24
25 // tela de configuraes aberta
26 }
27 // casos de testes aqui...
28 }
Figura 8.5: Exemplo de teste com a ferramenta Fest de uma tela sem o padro Tela como Unidade.
1 // junit
2 import org.junit.Before;
3 // FEST
4 import org.fest.swing.fixture.FrameFixture;
5
6 public class UmTest {
7 private FrameFixture window;
8
9 @Before
10 public void abreTelaEmTeste() {
11 window = new FrameFixture(new TelaConfiguracoesFrame());
12 window.show();
13 window.focus();
14 // tela de configuraes aberta
15 }
16 // casos de testes aqui...
17 }
Figura 8.6: Refatorao do exemplo da Figura 8.5 para utilizar o padro Tela como Unidade.
132
1 // referncias do JUnit
2 import org.junit.Before;
3 // referncias do WebDriver
4 import org.openqa.selenium.By;
5 import org.openqa.selenium.WebDriver;
6 import org.openqa.selenium.WebElement;
7
8 public class LoginTests {
9 public static final HOST = "http://localhost:8000";
10
11 @Before
12 public void abreTelaDeLogin() {
13 // Cria um cliente (navegador)
14 WebDriver driver = new HtmlUnitDriver();
15 // Abre uma pgina armazenada em um servidor local
16 driver.get(HOST);
17 // Procura o link para pgina de autenticao
18 WebElement login_link = driver.findElement(By.id("login"));
19 login_link.click();
20 // tela de login aberta
21 }
22
23 // casos de testes aqui
24 }
Figura 8.7: Exemplo de teste de uma pgina Web de autenticao sem o padro Tela como Unidade.
1 // referncias do JUnit
2 import org.junit.Before;
3 // referncias do WebDriver
4 import org.openqa.selenium.WebDriver;
5
6 public class LoginTests {
7 public static final HOST = "http://localhost:8000";
8
9 @Before
10 public void abreTelaDeLogin() {
11 // Cria um cliente (navegador)
12 WebDriver driver = new HtmlUnitDriver();
13 // Abre uma pgina de login diretamente
14 driver.get(HOST + "/login");
15 // tela de login aberta
16 }
17
18 // casos de testes aqui
19 }
Figura 8.8: Refatorao do exemplo da Figura 8.7 para utilizar o padro Tela como Unidade.
133
8.4.2 Estado Inicial da Tela
Tipo: Organizacional
Quando utilizar: Idealmente, em todos os testes de interface de usurio. Se muitos casos de testes
necessitarem de um mesmo estado diferente do inicial, este padro ainda dever ser usado, mas o
set up nessa situao necessita ter um cdigo adicional que alterar o estado da interface para o
desejado.
Inteno: Facilitar o entendimento do cdigo-fonte e a escrita de testes independentes.
Motivao: A tela a ser testada geralmente aberta no mtodo de set up, no entanto, uma mesma
tela da interface de usurio pode ter vrios estados; por exemplo, com dados carregados ou com
conguraes especcas. Se a cada vez que o set up executado, a interface iniciada em um
estado diferente devido a informaes previamente carregadas, ento no ca claro para os testes
qual o estado da interface que est sendo testada. Isso ajuda a tornar os testes intermitentes e
rebuscados.
Soluo: Padronizar que o mtodo de set up sempre ir carregar o estado inicial da tela em teste, ou
seja, todas as informaes que podem inuenciar o estado da tela devem ser apagados. Se for
necessrio fazer alguma modicao no estado inicial para realizao de algum cenrio, essa
modicao dever ser feita nos respectivos mtodos de teste. Se muitos casos de testes precis-
arem de um mesmo estado diferente do inicial, ento os testes podem ser agrupados em outra
classe que denir o estado desejado como estado inicial da tela.
Consequncias: O mtodo de set up se torna mais consistente, j que a tela sempre ser a mesma e
com o mesmo estado aps a sua execuo. Isso torna os testes mais fceis de serem entendidos,
pois evitada a adio de estruturas de controle de uxo para lidar com cada estado da tela.
Ainda, quando o estado precisar ser modicado para realizao de um teste, o trecho de cdigo
ca explcito no mtodo de teste, o que facilita o entendimento do caso de teste especco e no
prejudica a legibilidade dos outros casos.
Implementao: O padro Tela como Unidade pode ser o suciente para carregar uma tela no seu
estado inicial, pois evita que dados sejam lidos e processados durante a navegao por outras telas
do sistema em teste. Se no for suciente, devem ser desmantelados os dados j carregados no
mtodo de set up antes mesmo de exibir a tela.
Quando muitos testes possurem comportamento inicial repetido, devem ser feitas duas refa-
toraes: (1) a congurao do estado pode ser movida para um mtodo separado que ser
chamado por cada caso de teste; ou (2) pode ser criada uma nova classe especca para estes
casos de testes que podem compartilhar um mtodo de set up idntico.
Implementao - WUI: No caso de sistemas Web necessrio ateno com os dados carregados no
escopo da aplicao e na sesso de usurio, pois podem inuenciar o estado das telas. Ainda,
se for necessrio navegar entre algumas pginas, evite submeter formulrios desnecessrios para
evitar o carregamento de novas informaes.
O carregamento implcito de informaes no pertinentes aos casos de testes pode produzir falsos
resultados positivos ou negativos, principalmente quando as vericaes so pouco precisas; por
exemplo, quando vericado se uma determinada palavra aparece em qualquer local da tela.
Exemplo: A Figura 8.9 mostra uma organizao comum de testes de interface, mas que no se preocupa
com o estado da tela no momento do teste. J a Figura 8.10 apresenta solues simples que
garantem que no incio de cada teste o estado da tela dever ser sempre o mesmo.
134
1 // referncias do JUnit
2 import org.junit.BeforeClass;
3 // referncias do WebDriver
4 import org.openqa.selenium.WebDriver;
5
6 public class MapaDoSiteTests {
7 public static final HOST = "http://localhost:8000";
8
9 // A pgina em teste acessada apenas uma vez.
10 // Todos os testes iro manipular a interface, ento no claro
11 // qual o estado inicial da interface no incio de cada teste.
12 @BeforeClass
13 public void acessaPaginaDoMapaDoSite() {
14 WebDriver driver = new HtmlUnitDriver();
15 // Acessou uma pgina diferente da que est sendo testada.
16 // Informaes adicionais e irrelevantes para os testes
17 // podem ter sido carregadas desnecessariamente.
18 driver.get(HOST);
19 // Acessa a pgina que ser testada.
20 WebElement login_link = driver.findElement(By.id("login"));
21 login_link.click();
22 }
23
24 // Testes...
25 }
Figura 8.9: Exemplo de organizao sem o padro Estado Inicial da Tela.
1 // referncias do JUnit
2 import org.junit.BeforeClass;
3 // referncias do WebDriver
4 import org.openqa.selenium.WebDriver;
5
6 public class MapaDoSiteTests {
7 public static final HOST = "http://localhost:8000";
8
9 // Pgina acessa antes de cada teste, garantindo que todas as
10 // manipulaes (sem estado) feitas por testes anteriores
11 // sero descartadas.
12 @Before
13 public void acessaPaginaDoMapaDoSite() {
14 WebDriver driver = new HtmlUnitDriver();
15 // Pgina acessada diretamente.
16 driver.get(HOST + "/mapadosite");
17
18 // Uma maneira radical de forar que uma nova sesso seja utilizada
19 // para cada um dos testes.
20 driver.manage().deleteAllCookies();
21 }
22
23 // Testes ...
24 }
Figura 8.10: Exemplo de organizao com o padro Estado Inicial da Tela.
135
Padres Relacionados: O padro Tela como Unidade (Seo 8.4.1) ajuda a garantir que o mnimo de
informaes necessrias sero carregadas para o teste, facilitando a implementao desse padro.
Usos Conhecidos: Os testes dos sistemas Janus e GinLab seguem esta organizao.
136
8.4.3 Camada de Abstrao de Funcionalidades
Tipo: Organizacional
Quando utilizar: Idealmente, para todos os testes de interface de usurio. A camada de abstrao de
funcionalidades, descrita no padro, pode ser compartilhada pelos testes de correo da interface,
leiaute, usabilidade e acessibilidade.
Inteno: Separar o cdigo da manipulao da interface daquele que descreve os cenrios de testes para
facilitar a escrita e o entendimento dos testes.
Motivao: Muitos casos de testes da interface so descritos por meio de vericaes realizadas aps
a execuo de uma ou mais funcionalidades do sistema. No entanto, a chamada de uma fun-
cionalidade do sistema via interface grca , geralmente, representada por diversos comandos de
um usurio. Por isso, o cdigo-fonte que contm os cenrios de testes de interface tende a car
extenso e pouco legvel.
Alm disso, muitos cenrios de testes utilizam o mesmo conjunto de aes que manipulam a inter-
face, mas com pequenas diferenas. Portanto, natural a separao destas aes para reutilizao
de cdigo-fonte.
Soluo: Separar os casos de testes em duas camadas: uma de abstrao de funcionalidades, que ir
manipular a interface como um usurio, e outra, que ir fazer as chamadas das funcionalidades e
as vericaes necessrias na interface.
Consequncias: A abstrao das funcionalidades diminui a extenso do cdigo dos testes e melhora
a legibilidade. Esta modularizao tambm permite a melhor reutilizao do cdigo de acesso
interface, podendo at ser utilizada por baterias de tipos de testes diferentes.
Implementao: O teste deve conter apenas chamadas s funcionalidades da interface e vericaes.
Todo conjunto de aes de usurio que compe uma funcionalidade deve ser encapsulado em um
mtodo separado de uma classe independente dos casos de teste, ou seja, uma classe que pertence
camada de abstrao de funcionalidades. A Figura 8.11 mostra como ca a organizao do
cdigo-fonte dos testes utilizando esse padro.
Figura 8.11: Organizao recomendada de testes de interface de usurio.
Exemplo - WUI - Java/JUnit/WebDriver: A Figura 8.12 possui um exemplo de teste de uma tela de
autenticao de um sistema Web sem utilizar o padro Camada de Abstrao de Funcionali-
dades. Note que os testes so relativamente extensos para simplicidade do que se pretende veri-
car. Isso se deve s diversas aes necessrias para se autenticar no sistema.
Esses testes podem ser separados em duas camadas: a Camada de Abstrao de Funcionali-
dades como mostrado na Figura 8.13 e a de vericaes, que contm a descrio legvel dos
137
1 // referncias do JUnit
2 import org.junit.Test;
3 import static org.junit.Assert.*;
4 // referncias do WebDriver
5 import org.openqa.selenium.By;
6 import org.openqa.selenium.WebDriver;
7 import org.openqa.selenium.WebElement;
8
9 public class LoginTests {
10 public static final HOST = "http://localhost:8000";
11 // Cria uma instncia do navegador
12 WebDriver driver = new HtmlUnitDriver();
13
14 @Test
15 public void testaLoginValidoRedirecionaParaPaginaInicial() {
16 // Abre pgina de login
17 driver.get(HOST + "/login");
18 // Encontra objetos na tela
19 WebElement usuario = driver.findElement(By.id("usuario"));
20 WebElement senha = driver.findElement(By.id("senha"));
21 WebElement login = driver.findElement(By.id("login"));
22 // Simula aes do usurio
23 usuario.sendKeys("admin");
24 senha.sendKeys("1234");
25 login.click();
26 // Faz verificaes
27 assertTrue(driver.getCurrentUrl().endsWith("/home"));
28 assertTrue(driver.getPageSource().contains("Ol admin!"));
29 }
30
31 @Test
32 public void testaLoginInvalidoMostraMensagemDeErroNaMesmaTela() {
33 // Abre pgina de login
34 driver.get(HOST + "/login");
35 // Encontra objetos na tela
36 WebElement usuario = driver.findElement(By.id("usuario"));
37 WebElement senha = driver.findElement(By.id("senha"));
38 WebElement login = driver.findElement(By.id("login"));
39 // Simula aes do usurio
40 usuario.sendKeys("admin");
41 senha.sendKeys("senha_errada");
42 login.click();
43 // Faz verificaes
44 assertTrue(driver.getCurrentUrl().endsWith("/login"));
45 assertTrue(driver.getPageSource().contains("Login invlido."));
46 }
47
48 }
Figura 8.12: Exemplo de testes de uma pgina Web de autenticao sem utilizar o padro Camada de
Abstrao de Funcionalidades.
138
testes, como mostrado na Figura 8.14. Note que a camada de vericaes possui a mesma es-
trutura do exemplo original e apenas substitui os comandos de usurio por chamadas camada de
abstrao das funcionalidades.
J a camada de abstrao de funcionalidades deve apenas possuir mtodos ou funes que encap-
sulem um conjunto de aes que simulem a chamada de uma funcionalidade por um usurio do
sistema. Dessa forma, essa camada deve ser independente dos casos de teste; ou seja, os testes
devem ter conhecimento da camada de abstrao, mas no o contrrio. Alm disso, a camada no
deve possuir vericaes, pois no faz parte de seu objetivo testar o sistema.
1 // interessante separar a camada de abstrao das funcionalidades em um local
diferente
2 package funcionalidadesUI;
3
4 // referncias do WebDriver
5 import org.openqa.selenium.By;
6 import org.openqa.selenium.WebDriver;
7 import org.openqa.selenium.WebElement;
8 // Note que no h dependncias do arcabouo de teste porque no so feitas
verificaes nesta camada
9
10 public class Sistema {
11 WebDriver driver;
12 String host;
13
14 public Sistema(WebDriver driver, String host) {
15 this.driver = driver;
16 this.host = host;
17 }
18
19 public void login(String usuario, String senha) {
20 // Abre pgina de login
21 driver.get(host + "/login");
22 // Encontra objetos na tela
23 WebElement username = driver.findElement(By.id("username"));
24 WebElement password = driver.findElement(By.id("password"));
25 WebElement login = driver.findElement(By.id("login"));
26 // Simula aes do usurio
27 username.sendKeys(usuario);
28 password.sendKeys(senha);
29 login.click();
30 }
31 }
Figura 8.13: Refatorao do exemplo da Figura 8.12. Essa classe faz parte da Camada de Abstrao
de Funcionalidades.
Ainda, a camada de abstrao pode ser parametrizvel para que ela seja adaptvel a vrios casos
de teste sem replicao de cdigo. Tambm podem ser criados mtodos como acar sinttico
para as combinaes de parmetros mais comuns, o que facilita a escrita dos testes, assim como
melhora a sua legibilidade. AFigura 8.15 possui acares sintticos comuns que podemser usados
em diversos casos de testes e em diferentes baterias de testes.
Padres Relacionados: A inicializao das interfaces pode ser includa na camada de abstrao de
funcionalidades, dessa forma, essa organizao pode ser integrada com as recomendaes do
padro Tela como Unidade (Seo 8.4.1).
139
1 // referncias do JUnit
2 import org.junit.Test;
3 import static org.junit.Assert.*;
4 // referncias do WebDriver
5 import org.openqa.selenium.WebDriver;
6
7 public class LoginTests {
8 public static final HOST = "http://localhost:8000";
9 WebDriver driver = new HtmlUnitDriver();
10 Sistema sistema = new Sistema(driver, HOST);
11
12 @Test
13 public void testaLoginValidoRedirecionaParaPaginaInicial() {
14 sistema.login("admin", "123456");
15 // Faz verificaes
16 assertTrue(driver.getCurrentUrl().endsWith("/home"));
17 assertTrue(driver.getPageSource().contains("Ol admin!"));
18 }
19
20 @Test
21 public void testaLoginInvalidoMostraMensagemDeErroNaMesmaTela() {
22 sistema.login("admin", "senha_errada");
23 // Faz verificaes
24 assertTrue(driver.getCurrentUrl().endsWith("/login"));
25 assertTrue(driver.getPageSource().contains("Login invlido."));
26 }
27
28 }
Figura 8.14: Continuao da refatorao do exemplo da Figura 8.12. Camada que contm as vericaes
utilizando a Camada de Abstrao de Funcionalidades.
1 // ...
2 public class Sistema {
3
4 public void login(String usuario, String senha) {
5 // ...
6 }
7
8 public void logaComoAdministrador() {
9 login("admin", "123456");
10 }
11
12 public void logaComoUsuarioComum() {
13 login("user", "1234");
14 }
15 }
Figura 8.15: Acares sintticos para melhorar ainda mais a legibilidade dos testes.
140
Usos Conhecidos: O sistema Janus organiza seus testes de interface utilizando esse padro. A docu-
mentao da ferramenta WebDriver descreve o padro Objeto Pgina (Page Object)
8
, que descreve
como devem ser implementados os objetos da Camada de Abstrao de Funcionalidades.
8
http://code.google.com/p/selenium/wiki/PageObjects.
141
8.4.4 Fotograa da Interface
Tipo: Organizacional
Quando utilizar: Idealmente, em todos os casos de teste de interface grca de usurio.
Inteno: Facilitar a anlise dos testes que falham por meio de uma fotograa da interface que cap-
turada no momento em que o erro ocorre.
Motivao: Testes de interface podem ter resultados falso-negativos por muitas razes, como devido
a alteraes do leiaute, erros de implementao do teste ou, at mesmo, por problemas de in-
fraestrutura, para o caso de testes integrados. Por causa disso, pode ser demorado identicar o
motivo das falhas do teste, principalmente quando necessrio depurar o sistema. A fotograa
serve de complemento s outras informaes armazenadas pelo arcabouo de teste, como os val-
ores de variveis.
Soluo: Capturar uma fotograa da interface toda vez que um teste falhar, a qual dever ser o ponto
de partida para anlise do erro.
Consequncias: A fotograa pode facilitar o entendimento do que aconteceu de errado ou at mesmo
elucidar imediatamente a causa do erro.
Implementao: A implementao desse padro complexa, pois pode depender de detalhes do sis-
tema operacional, das bibliotecas de interface ou dos navegadores. Por isso, s recomendvel
utilizar esse padro com o auxlio de alguma ferramenta que disponibilize essa funcionalidade.
Contudo, algumas ferramentas de teste de interface j fornecem em sua biblioteca padro de fun-
cionalidades esse tipo de comando, como o caso da ferramenta Selenium.
Exemplo - WUI Java/Selenium/Util4Selenium: A ferramenta Util4Selenium disponibiliza um as-
pecto que facilita a implementao desse padro de modo que reduzido drasticamente a repli-
cao de cdigo-fonte. A Figura 8.16 mostra um trecho da implementao desse aspecto. Todos
os mtodos que forem anotados com o metadado @Screenshot recebero um cdigo que auto-
maticamente capturar uma fotograa da interface Web caso uma exceo seja lanada. Quando
uma classe possuir essa informao, ento o mesmo cdigo adicionado a todos os seus mtodos.
Um bom modo de utilizar essa ferramenta com a criao de uma classe base (Figura 8.17) que
dever ser herdada por todas as classes de testes de interface do sistema (Figura 8.18). Como
ela possui a anotao @Screenshot, ento o padro Fotograa da Interface ser automaticamente
propagado para todos os casos de teste do sistema.
Padres Relacionados: Esse padro tambm pode ser utilizado na Camada de Abstrao de Fun-
cionalidades (Seo 8.4.3).
Usos Conhecidos: A ferramenta Selenium disponibiliza uma funcionalidade que bate uma fotograa
da pgina renderizada no navegador Web e a Util4Selenium utiliza essa funcionalidade de forma
automtica, por exemplo, ela pode ser congurada para bater fotos toda vez que um caso de teste
falhar, como mostrado no exemplo anterior.
142
1 // Referncias Java e AspectJ
2 import org.aspectj.lang.reflect.MethodSignature;
3 import java.lang.annotation.Annotation;
4 import org.aspectj.lang.Signature;
5 // Outras referncias foram ocultas
6
7 public aspect AspectScreenshotByAnnotation {
8 // Qualquer mtodo ou classe anotado com @Screenshot
9 pointcut annotationHandler():
10 if(System.getProperty("selenium.screenshot") != null &&
11 System.getProperty("selenium.screenshot").equals("true")) &&
12
13 // Mtodo
14 // @Screenshot (void|objeto) umMetodo(argumentos): public,private..
15 (execution(@utilities.util4selenium.annotations.Screenshot * *(..)) ||
16
17 // Classe
18 // (public/private..) (@Screenshot UmaClasse).umMetodo(argumentos)
19 (execution(* (@utilities.util4selenium.annotations.Screenshot *).*(..)) &&
20 !execution(public * selenium())));
21
22 after() throwing(): annotationHandler() {
23 Signature signature = thisJoinPoint.getSignature();
24 MethodSignature methodSignature = (MethodSignature) signature;
25 Annotation annotation = methodSignature.getMethod().getAnnotation(Screenshot.
class);
26 if(annotation == null) {
27 annotation = signature.getDeclaringType().getAnnotation(Screenshot.class);
28 }
29 if(annotation != null && thisJoinPoint.getThis() instanceof SeleniumClass) {
30 SeleniumClass obj = (SeleniumClass) thisJoinPoint.getThis();
31 ScreenshotHelper helper = new ScreenshotHelper(obj.selenium());
32 String clazz = signature.getDeclaringType().getSimpleName();
33 String method = signature.getName();
34 helper.screenshot(clazz + "-" + method);
35 }
36 }
37 }
Figura 8.16: Aspecto fornecido pela biblioteca Util4Selenium para bater fotograas da interface.
143
1 // Classe base pra todas clases de teste da interface Web
2
3 // Referncias do TestNG
4 import org.testng.annotations.AfterSuite;
5 import org.testng.annotations.BeforeSuite;
6 // Referncias do Util4Selenium
7 import testutilities.util4selenium.annotations.Screenshot;
8 import testutilities.util4selenium.annotations.Screenshot.ScreenshotPolicy;
9 // Referncias do Selenium-RC/Java
10 import com.thoughtworks.selenium.DefaultSelenium;
11 import com.thoughtworks.selenium.Selenium;
12
13 // Padro Fotografia da Interface
14 @Screenshot(policy = ScreenshotPolicy.ON_ERROR)
15 public class SeleniumTestCase {
16 public Selenium navegador;
17
18 @BeforeSuite public void abreNavegador() {
19 navegador = new DefaultSelenium(
20 "localhost", 4444,
21 "*chrome", "http://localhost:8000");
22 navegador.start();
23 }
24
25 @AfterSuite public void fechaNavegador() {
26 navegador.stop();
27 }
28 }
Figura 8.17: Classe base que ativa o padro Fotograa da Interface.
1 // Referncias do JUnit + Hamcrest
2 import org.junit.*;
3 import static org.junit.Assert.*;
4 import static org.hamcrest.Matchers.*;
5 // Referncias do Selenium-RC/Java
6 import org.testng.annotations.Test;
7
8 public class MapaDoSiteTests extends SeleniumTestCase {
9
10 @Before public void acessaPaginaEmTeste() {
11 navegador.open("/mapadosite");
12 }
13
14 @Test public void verificaLinksImportantes() {
15 // Se falhar, ser capturada uma fotografia da interface e
16 // salva no arquivo MapaDoSiteTests-verificaLinksImportantes.png
17 }
18 }
Figura 8.18: Exemplo de classe de teste que utiliza a classe base SeleniumTestCase.
144
8.4.5 Localizar Elemento por ID
Tipo: Robustez
Quando utilizar: Em todos os testes, principalmente para localizao dos principais componentes da
interface.
Inteno: Tornar a localizao de componentes na interface independente do leiaute, do estado dos
componentes e da internacionalizao (i18n) do sistema.
Motivao: A localizao dos componentes da interface uma tarefa delicada. Os testes no devem
ser frgeis a tal ponto de quebrarem por qualquer alterao do leiaute.
Soluo: Atribuir IDs aos principais componentes da interface de usurio para facilitar a localizao
dos mesmos. Apenas necessrio cuidado para que mais de um componente no possua ID
idntico, j que uma mesma tela pode conter diversos painis que possuem campos semelhantes.
Uma forma de organizao que evita que isso acontea concatenar os nomes dos painis ou das
telas como prexo do ID do componente.
Consequncias: O cdigo de localizao dos elementos pode car mais claro, pois no necessrio
o uso de expresses de busca como DOM e XPath. Alm disso, o identicador tem como pre-
missa ser uma propriedade nica e exclusiva de um elemento, portanto facilita a identicao dos
componentes.
Implementao: Os componentes que sero manipulados precisam ter IDs denidos pelos progra-
madores. Apesar disso, algumas ferramentas de testes permitem que se dena os IDs em tempo
de execuo do teste. Neste caso, recomendado separar a denio dos IDs em um local isolado
para no prejudicar a legibilidade dos testes. Um bom local para denio dos IDs na Camada
de Abstrao de Funcionalidades.
importante notar que existem arcabouos de interface de usurio que geram IDs dinamicamente
para os componentes, contudo esses IDs podem prejudicar a legibilidade dos testes. Para que
no sejam gerados identicadores repetidos, os respectivos algoritmos concatenam caracteres adi-
cionais e no intuitivos que podem criar ambiguidade no entendimento dos cenrios de teste.
Exemplo: A Figura 8.19 mostra um exemplo do padro com a ferramenta WebDriver e HTMLUnit.
1 // Cria um cliente (navegador)
2 WebDriver driver = new HtmlUnitDriver();
3 // Procura um link pelo ID
4 WebElement link = driver.findElement(By.id("mapadosite"));
Figura 8.19: Exemplo de localizao de um elemento por ID com WebDriver e HTMLUnit.
Padres Relacionados: O padro Localizar Elemento por Tipo do Componente (Seo 8.4.6) serve
de alternativa para quando esse padro no puder ser utilizado.
Usos Conhecidos: um padro das ferramentas de teste de interface fornecer mecanismos para lo-
calizar elementos por um identicador.
145
8.4.6 Localizar Elemento por Tipo do Componente
Tipo: Robustez
Quando utilizar: Quando o componente a ser localizado no possui um ID, ou, ento, quando o tipo
do componente utilizado importante para o entendimento do teste.
Inteno: Tornar explcito o tipo de componente no cdigo do teste para facilitar a compreenso.
Motivao: Em algumas situaes, mais fcil de entender um caso de teste quando sabemos quais os
tipos dos componentes que esto sendo manipulados. Quando utilizamos apenas o ID, o tipo do
componente abstrado.
Soluo: Localizar um componente pelo seu tipo. No entanto, quando a tela possuir mais de um com-
ponente do mesmo tipo, outras propriedades devem ser usadas para ltrar apenas os elementos
desejados. Nesses casos, d preferncia para propriedades que no estejam relacionadas com o
leiaute da tela.
Consequncias: O cdigo de localizao dos elementos tende a car mais extenso e mais preso aos
tipos dos componentes utilizados. Contudo, o teste pode car mais fcil de ser entendido.
Implementao: Algumas ferramentas disponibilizam mtodos genricos, que buscam qualquer tipo
de componente. J outras possuem mtodos que buscam um tipo de componente especco. Para
esse ltimo, caso pode-se utilizar apenas o identicador do componente.
Implementao - WUI: Geralmente, as ferramentas permitem localizar elementos por expresses
XPath. Para explicitar que o componente uma caixa de texto, poderia-se utilizar a seguinte
expresso: input[type="text", id="id_do_componente"]. Este tipo de expresso pode en-
contrar mais de um elemento, por isso a necessidade do ID. Ainda h a possibilidade de utilizar
outras propriedades, mas devem ser evitadas, pois isso poderia tornar o teste frgil em relao a
alteraes do leiaute.
Exemplo: A Figura 8.20 mostra um exemplo com a ferramenta WebDriver e HTMLUnit para buscar
um elemento na rvore HTML. A busca feita por meio de uma expresso XPath, que muito
exvel e aceita tanto como parmetros de leiaute como de propriedades do componente. Contudo,
a expresso contm apenas informaes referentes ao tipo de componente e de propriedades que
so nicas do elemento.
1 // Cria um cliente (navegador)
2 WebDriver driver = new HtmlUnitDriver();
3 // Procura um link pelo seu tipo e por suas propriedades, com XPath.
4 WebElement link = driver.findElement(By.xpath("//a[@href=/mapadosite]"));
Figura 8.20: Exemplo de localizao de um elemento pelo tipo com WebDriver e HTMLUnit.
Padres Relacionados: Estratgia alternativa para o padro Localizar Elemento por ID (Seo 8.4.5).
Usos Conhecidos: um padro das ferramentas de testes de interface Web, tais como Selenium e
HTMLUnit, fornecer mecanismos para localizar os elementos atravs de XPath.
146
8.4.7 Localizar Clula de Tabela pelo Cabealho e Contedo
Tipo: Robustez
Quando utilizar: Em testes que fazem vericaes em clulas especcas de tabelas.
Inteno: Tornar a localizao de clulas de tabelas menos frgil em relao a alteraes de leiaute ou
a mudana dos dados.
Motivao: Muitas vezes necessrio vericar o contedo ou obter componentes de clulas especcas
de tabelas. Um modo de acessar clulas especcas a partir dos ndices da linha e coluna (Figura
8.21), mas essa abordagem tornam os testes muito frgeis. Qualquer alterao do leiaute ou novo
registro carregado pode quebrar os testes.
1 // Referncias do HTMLUnit
2 import com.gargoylesoftware.htmlunit.html.HtmlPage;
3 import com.gargoylesoftware.htmlunit.html.HtmlTable;
4 import com.gargoylesoftware.htmlunit.html.HtmlTableCell;
5
6 public class HTMLTableHelper {
7 public HtmlTableCell buscaCelulaPeloLayout(HtmlPage pagina, String idTabela, int
linha, int coluna) {
8 HtmlTable tabela = pagina.getHtmlElementById(idTabela);
9 return tabela.getCellAt(linha, coluna);
10 }
11 }
Figura 8.21: Busca da clula de uma tabela pelo leiaute.
Soluo: Identicar o ndice da clula por meio de um algoritmo que percorra todas as linhas de uma
coluna com determinado cabealho at que o contedo da clula corrente seja o desejado. Quando
os dados da tabela esto organizados por colunas em vez de linhas, o algoritmo deve seguir a
mesma ideia, mas percorrendo todas as colunas de uma linha.
Consequncias: Apesar de os testes perderem desempenho, eles cam mais resistentes a alteraes do
leiaute. A legibilidade do teste tambm melhora, pois os nmeros dos ndices so substitudos por
strings que so mais intuitivas.
Implementao: Algumas ferramentas j disponibilizam mtodos prontos para facilitar a busca de
clulas. Quando utilizada uma que no possui esta facilidade, importante criar um mtodo
independente do caso de teste para realizao dessa tarefa. Dessa forma, o mtodo pode ser re-
utilizado para outros casos de teste e at mesmo ser enviado como sugesto para a equipe da
ferramenta.
Este mtodo deve receber o identicador da tabela e uma informao da linha e uma da coluna.
Supondo que cada linha da tabela representa um registro, a informao da coluna pode ser o ID
da clula de cabealho, que, geralmente, a primeira da tabela. Para identicar a linha, pode
se passar parte do contedo esperado, como textos, componentes ou expresses regulares. Outra
abordagem mais orientada a objetos seria criar as estruturas Tabela, Linha, Coluna e Clula. Com
isso, o algoritmo pode car mais modularizado e exvel.
Algumas tabelas so mais complexas, podendo conter outras tabelas internas ou outras formas de
diviso. Se no for possvel seguir este padro completamente, pode ser interessante utiliz-lo
em conjunto com ndices. Dessa forma, reduzido o uso de ndices das clulas, tornando o teste
menos frgil.
147
Exemplo: A ferramenta HTMLUnit j fornece diversos mtodos para leitura de tabelas que facilitam
bastante a criao dos testes, contudo, ainda possvel criar mtodos auxiliares para forar o uso
desse padro. A Figura 8.22 mostra duas funcionalidades que percorrem as clulas da tabela em
busca da clula desejada para manipulao e vericao. Apenas importante notar que essas
funcionalidades nem sempre podem ser utilizadas, pois elas no consideram a complexidade das
tabelas e tambm supe que as clulas possuem contedo nico.
1 // Referncias do HTMLUnit
2 import com.gargoylesoftware.htmlunit.html.HtmlPage;
3 import com.gargoylesoftware.htmlunit.html.HtmlTable;
4 import com.gargoylesoftware.htmlunit.html.HtmlTableCell;
5 import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
6
7 public class HTMLTableHelper {
8 public HtmlTableCell buscaCelulaPorConteudo(HtmlPage pagina, String idTabela,
String conteudoLinha) {
9 HtmlTable tabela = pagina.getHtmlElementById(idTabela);
10 for(HtmlTableRow linha: tabela.getRows()) {
11 for(HtmlTableCell celula: linha.getCells()) {
12 if(celula.asText().contains(conteudoLinha))
13 return celula;
14 }
15 }
16 throw new RuntimeException("Clula com contedo " + conteudoLinha + " no
encontrada.");
17 }
18
19 public HtmlTableCell buscaCelulaPorCabecalhoEConteudo(HtmlPage pagina, String
idTabela, String idCabecalho, String conteudoLinha) {
20 HtmlTable tabela = pagina.getHtmlElementById(idTabela);
21
22 HtmlTableRow linhaCabecalho = tabela.getRow(0);
23 List<HtmlTableCell> celulasCabecalho = linhaCabecalho.getCells();
24 int indiceColunaReferencia = -1;
25 for(HtmlTableCell celula: celulasCabecalho) {
26 indiceColunaReferencia += 1;
27 if(celula.getId().equals(idCabecalho))
28 break;
29 }
30 if(indiceColunaReferencia == -1 || indiceColunaReferencia == celulasCabecalho.
size())
31 throw new RuntimeException("Cabealho de ID " + idCabecalho + " no
encontrado.");
32
33 for(HtmlTableRow linha: tabela.getRows()) {
34 HtmlTableCell celula = linha.getCell(indiceColunaReferencia);
35 if(celula.asText().contains(conteudoLinha))
36 return celula;
37 }
38 throw new RuntimeException("Clula com contedo " + conteudoLinha + " no
encontrada.");
39 }
40 }
Figura 8.22: Exemplo de Localizar Clula pelo Cabealho e Contedo com o HTMLUnit.
Padres Relacionados: Os padres Localizar Elemento por ID (Seo 8.4.5) e Localizar Elemento
148
por Tipo do Componente (Seo 8.4.6) devem ser utilizados para implementao desse padro.
Usos Conhecidos: Sistema GinLab e Janus implementame utilizamesse padro. As ferramentas HTM-
LUnit e Util4Selenium tambm fornecem essas funcionalidades.
8.5 Antipadres
Nas subsees seguintes sero descritos antipadres de automao para testes que envolvem a interface
de usurio, seguindo o esqueleto apresentado na Seo 5.4. Os indcios de problemas citados nos padres
so descritos na Seo 5.2.
8.5.1 Navegao Encadeada
Tipo: Organizacional
Contexto: Para testar uma tela pode ser necessrio a navegao entre diversas telas do sistema. Pen-
sando em desempenho e praticidade para criao dos testes, pode-se criar um grande teste que
vai vericando as telas medida que elas vo sendo carregadas. Essa prtica tende a criar testes
muito extensos, complexos e ilegveis. Isso os torna difceis de manter e entender, alm de que
alteraes em uma das telas pode atrapalhar os testes de outras.
Indcios de Problemas Relacionados: Obscure Test, Test Code Duplication, Erratic Test, Fragile Test,
Frequent Debugging, Slow Tests, Buggy Tests, Developers Not Writing Tests e High Test Mainte-
nance Cost (vide Seo 5.2).
8.5.2 Localizar Componente pelo Leiaute
Tipo: Robustez
Contexto: Dependendo das ferramentas de teste possvel localizar um componente de muitas
maneiras. Uma das alternativas utilizar propriedades como a posio na tela ou, ainda, a or-
dem de exibio. No entanto, qualquer alterao no leiaute pode quebrar o teste, mesmo que
o sistema esteja correto. Como todo teste que falha requer uma avaliao, os testes quebrados,
que so falso-negativos, desperdiam tempo de desenvolvimento com a depurao dos testes e do
sistema.
Indcios de Problemas Relacionados: Obscure Test, Fragile Test, Frequent Debugging e High Test
Maintenance Cost (vide Seo 5.2).
8.5.3 Vericaes Rgidas
Tipo: Robustez
Contexto: Existem ferramentas ou tnicas que fazem vericaes muito rgidas na interface de usurio.
Por exemplo, h ferramentas que fazem anlises pixel a pixel para vericar se houveram alter-
aes. Outra abordagem transforma o cdigo da interface em um hash, que pode ser comparado
com o mesmo propsito. Estas abordagens so muito inexveis, pois qualquer refatorao e at
identao do cdigo-fonte pode quebrar os casos de teste.
Indcios de Problemas Relacionados: Fragile Test, Developers Not Writing Tests e High Test Mainte-
nance Cost (vide Seo 5.2).
149
8.6 Concluses
Para fazer testes de interface fundamental a utilizao de arcabouos de testes especializados. No
obstante, a qualidade dos testes automatizados que simulam usurios dependem da boa abstrao e das
facilidades que os arcabouos de testes de interface disponibilizam. Tambm importante notar que
essas ferramentas so geralmente complexas de implementar, por isso muitas ainda possuem limitaes.
As boas ferramentas de gravao de interao dos usurios so muito teis para criao de vrios
tipos de testes que envolvem a interface de usurio. No entanto, necessrio ateno com o cdigo-fonte
por elas gerado para que os testes automatizados no sejam de baixa qualidade, prejudicando e atrasando
o desenvolvimento do sistema.
O desempenho dos testes de interface tambm importante. recomendvel utilizar ferramentas
leves e criar testes completamente independentes para que seja possvel execut-los em paralelo. Bate-
rias de testes independentes podem ser executadas em diversos ambientes dinstintos, por exemplo, para
buscar erros de portabilidade.
Alm disso, h diversos padres e antipadres que podem inuenciar signicativamente na organi-
zao e na robustez dos testes automatizados, o que fundamental para que a automao dos testes seja
bem feita e ajude no dia a dia do desenvolvimento de sistemas.
150
Captulo 9
Tcnicas de Desenvolvimento de Software
com Testes Automatizados
Como foi discutido nos captulos anteriores, as baterias de testes automatizados precisam estar bem
implementadas para que o custo-benefcio dessa prtica seja baixo. Assim como o cdigo-fonte do
sistema, o dos testes automatizados tambm est sujeito a imperfeies que podem trazer malefcios
graves para um projeto. Por isso, importante o conhecimento de boas prticas e padres que auxiliem
na criao de testes automatizados de qualidade.
Existem diversas tcnicas de desenvolvimento de software com testes automatizados que inuen-
ciam diretamente na qualidade dos testes e do sistema. Essas tcnicas so descritas por processos sim-
ples e sistemticos. Basicamente, elas denem a relao dos testes automatizados com o processo de
desenvolvimento e propem um roteiro de quando implementar os casos de teste de correo.
Dentre as tcnicas que sero descritas esto TAD, TFD, TDD e BDD, que j foram citadas no
Captulo 3. Entretanto, sero apresentadas as vantagens e as desvantagens de cada tcnica, assim como
algumas comparaes entre elas.
Apesar de as tcnicas citadas neste captulo serem generalizadas para qualquer tipo de teste (vide
a NASA que usava o ciclo de TDD para cartes perfurados [84]), ser evidenciado apenas os testes
automatizados de unidade, com exceo do caso de BDD que incluir testes de aceitao.
Os testes de correo de interface de usurio tambm podem ser escritos com as tcnicas citadas,
mas algumas delas so incompatveis com certas ferramentas de testes de interface. Por exemplo, TDD e
TFD requerem que os testes sejam feitos antes da implementao, o que inviabiliza o uso de gravadores
de interao.
9.1 Testes Aps a Implementao (TAD)
Testar aps a implementao (TAD de Test After Development) a tcnica de implementar e executar os
testes depois que um ou todos os mdulos de um sistema esto nalizados (Figura 9.1). Esse o modo
convencional e natural da abordagem dos testes dinmicos (vide Seo 3.1), j que para executar testes
em tempo de execuo necessrio que o sistema ou parte dele esteja implementado.
Quando os testes so implementados aps classes ou mtodos serem nalizados, TAD pode inuen-
ciar signicativamente o cdigo-fonte e a arquitetura do sistema devido ao rpido feedback dos testes.
J quando os testes so realizados apenas ao trmino do desenvolvimento, TAD tende a se tornar uma
prtica de controle e garantia de qualidade para as unidades do sistema.
Todavia, a proposta principal de TAD fazer vericaes no sistema. TAD no s surgiu em con-
junto com testes automatizados e arcabouos de teste, como tambm herdou a caracterstica da abor-
dagem tradicional de testes manuais de software de realizar os testes aps a implementao. No faz
151
Figura 9.1: Fluxo do TAD.
parte da proposta principal de TAD inuenciar na criao de cdigo-fonte do sistema.
Fazer os testes aps a implementao coerente, j que sistemas sem testes podem funcionar corre-
tamente, mas testes sem sistema no fazem sentido; portanto, o ideal sempre priorizar a implementao
do sistema (mesmo nos dias de hoje). Apesar disso, essa abordagem, assim como as outras, no a mais
indicada para todas as situaes e projetos. A seguir sero apresentadas as principais caractersticas de
TAD e quando recomendamos sua utilizao.
Caractersticas
Quando os testes so implementados aps um trecho do sistema ser nalizado, eles precisam se adaptar
estrutura de cdigo j existente. Se a testabilidade do cdigo alta, ento no h grandes problemas
durante a automao. Os testes no inuenciam no design do sistema, mas se tornam teis para veri-
cao. Entretanto, se a testabilidade do cdigo baixa, ento a automao de testes pode seguir por
vrios caminhos.
A primeira opo parar a automao de teste at que o sistema seja refatorado para melhorar a
estrutura e aumentar a testabilidade. Nesse caso, a automao no efetivamente concretizada, mas ela
serviu para fornecer feedback sobre a modelagem do sistema.
No entanto, se os testes automatizados forem realmente implementados, ento pode ser necessrio
o uso do antipadro Ganho para os Testes (Seo 6.5.1), que suja o cdigo do sistema, para contornar
as diculdades causadas pelo design acoplado. Caso esse antipadro no seja aplicado, ento o cdigo
dos testes que se tornaro rebuscados. Como os testes precisam contornar as falhas de testabilidade
para conseguir simular os cenrios desejados, seu cdigo-fonte provavelmente car mais extenso e,
consequentemente, com pior legibilidade e clareza.
Por causa disso, a implementao e a manuteno dos testes cam mais complexas e, portanto, mais
cara. Esta queda do custo-benefcio da automao dos testes pode resultar na diminuio do escopo das
vericaes, ou seja, cenrios importantes podem deixar de ser realizados e a cobertura dos testes tende
a diminuir.
A falsa impresso de o sistema estar nalizado e correto, em conjunto com a diculdade de criar os
casos de teste, pode levar a equipe de desenvolvimento ou gerentes a crer que a automao dos testes
desnecessria. Por isso, importante lembrar que o alto custo de manuteno dos sistemas sem testes
automatizados no se deve apenas fase de desenvolvimento, mas, principalmente, a mdio e longo
prazo.
152
De qualquer maneira, em casos extremos de diculdade, a automao de testes pode ser realmente
desnecessria. Em situaes em que a testabilidade muito baixa, mais vantajoso fazer testes manuais
e integrados que consigam simular os principais casos de testes. Apenas importante notar que qualquer
cenrio que diminua as tarefas de vericao do sistema torna propcio a identicao de erros em
ambientes de produo.
Quando Utilizar
O fato de TAD possuir fases distintas para implementar e testar uma vantagem para situaes espec-
cas, como para a manuteno de sistemas legados. Como a automao de testes ainda uma prtica
recente, muitos sistemas legados que precisam de manuteno no possuem baterias de testes automati-
zados.
J que a manuteno desses sistemas pode ser uma tarefa crtica, recomendvel que sejam feitos
pelo menos algumas baterias de testes automatizados antes de alterar o cdigo-fonte para certicar que
erros de regresso no sero adicionados [55]. Assim, TAD a abordagem mais recomendada para estes
cenrios. Contudo, aps a criao de um conjunto pertinente de casos de testes, podem ser utilizadas
outras tcnicas para implementar novos testes e alteraes do sistema.
Outra situao tpica em que TAD recomendada quando um sistema possui uma falha j identi-
cada. Antes corrigi-la, deve-se criar um caso de teste que reproduz o cenrio defeituoso. Este caso de
teste ajuda a identicar quando a tarefa de correo foi nalizada, e serve de precauo para que o erro
no ocorra novamente.
Testar depois da implementao tambm til para as equipes que esto comeando a aprender
testes automatizados e as ferramentas de teste. Como TAD possui como proposta principal apenas
testar o sistema, o estudo ca voltado para a automao de testes. Dessa forma, o aprendizado no
desvirtuado para a soluo de problemas, elaborao de arquitetura ou detalhes de implementao.
9.2 Testes a Priori (TFD)
Desenvolvimento com testes a priori (TFD de Test-First Development) a tcnica que prope implemen-
tar todos ou uma boa quantidade de casos de teste antes de desenvolver o cdigo-fonte de uma unidade
do sistema.
Para que esses testes sejam implementados, necessrio o conhecimento prvio do que ser a ar-
quitetura do sistema e a assinatura das classes em teste. Ainda, preciso o planejamento prvio dos
casos de testes.
Depois da implementao dos testes, invivel a execuo dos mesmos porque todos devem falhar,
j que nada foi implementado ainda. Alm disso, se a linguagem de programao utilizada tiver tipagem
esttica, o cdigo dos testes pode at mesmo no compilar. Assim, os testes s podero ser executados
aps a implementao dos trechos pertinentes do sistema, que devem seguir o design denido. A imple-
mentao do sistema deve ser feita at que todos os testes possam ser executados com sucesso. Depois
disso, as etapas so repetidas at que a fase de desenvolvimento termine. Esse uxo pode ser visualizado
na Figura 9.2.
A prtica de elaborar casos de testes independentemente da implementao do sistema no nova.
Muitas equipes de analistas de qualidades trabalham dessa forma, s que executam os testes aps o
sistema estar nalizado. Isso possvel porque, para elaborar os casos de testes, necessrio apenas o
conhecimento detalhado dos requisitos.
Contudo, a ideia de implementar os testes antes mesmo do sistema uma abordagem completamente
diferente do modelo tradicional de testes de software. Ela segue fortemente a proposta da preveno de
erros, j que, provavelmente, casos importantes de serem vericados no sero deixados de lado por
problemas com prazo ou por irresponsabilidade, pois o desenvolvimento se d at que todos os testes
153
Figura 9.2: Fluxo do TFD.
obtenham sucesso. Esse aumento da prioridade da vericao do sistema tende a melhorar a sua qual-
idade, assim como minimizar a quantidade de erros encontrados em fases posteriores do desenvolvi-
mento.
Caractersticas
Note que TFD, assim como TAD, sugere que a implementao dos testes e a do sistema sejam tarefas
que se complementem. Contudo, a ordem em que os testes e o sistema so implementados a oposta.
Essa diferena resulta em caractersticas bem peculiares de TFD em relao a TAD.
Da mesma forma em que com TAD os testes precisam se adaptar ao cdigo do sistema, com TFD
o cdigo do sistema induzido a se adaptar ao dos testes, o que resulta na criao de sistemas com
alta testabilidade. Apesar de os testes serem implementados com base em uma arquitetura previamente
denida, as estruturas de dados devem ser modicadas de acordo com o feedback de testabilidade dos
testes. Essas mudanas na arquitetura devem ser de fcil realizao porque o sistema ainda no foi
implementado.
Sendo assim, a automao de testes com TFD pode afetar signicativamente o desenvolvimento
dos sistemas de software. Contudo, imprescindvel o conhecimento de padres e boas prticas de
automao de testes para que o feedback gerado inuencie positivamente no cdigo do sistema. Se os
testes forem mal implementados, provavelmente as falhas de design no sero identicadas.
Alm disso, implementar os testes antes ou depois do sistema no impede que antipadres sejam
utilizados. Aprincipal vantagemde implementar os testes antes do cdigo do sistema deve-se liberdade
para criao de testes implementados de qualidade, ou seja, os antipadres so mais facilmente evitados,
pois no necessrio adaptar o cdigo dos testes a uma arquitetura indesejada do sistema.
Uma boa maneira para evitar antipadres manter o cdigo dos testes o mais simples possvel. Se
os casos de testes estiverem complicados de se elaborar ou de se implementar, um indcio de que algo
do design do sistema ou da unidade precisa ser melhorado. Por exemplo, pode ser um sinal de que a
unidade possui mais de uma responsabilidade, ou de que existe uma intimidade inapropriada entre elas.
J quando a unidade em teste est difcil de ser isolada, um indcio de que no possvel injetar as
dependncias, ou seja, que a construo do objeto est mal implementada.
Contudo, um dos maiores benefcios de se realizar os testes antes da implementao o fato de que
o desenvolvedor forado a reetir sobre o comportamento esperado do sistema e o que pode dar errado
antes mesmo de implement-lo.
154
9.3 Desenvolvimento Dirigido por Testes (TDD)
Desenvolvimento Dirigido por Testes (TDD de Test-Driven Development) uma tcnica de desenvolvi-
mento de software que se d pela repetio disciplinada de um ciclo curto de passos de implementao
de testes e do sistema (Figura 9.3) [82]. Esta tcnica foi descrita em 2002 por Kent Beck no livro
Test-Driven Development: By Example [16].
Figura 9.3: Ciclo de TDD.
O ciclo de TDD denido por trs passos:
1. Implementar apenas um caso de teste;
2. Implementar apenas um trecho de cdigo de produo que seja suciente para o novo caso de
teste ter sucesso de tal modo que no quebre os testes previamente escritos;
3. Se necessrio, refatorar o cdigo produzido para que ele que mais organizado. Vale notar que
refatorao no altera a funcionalidade do sistema e, portanto, no deve quebrar os testes j im-
plementados [59, 14, 122, 79]. Ainda, a refatorao pode ser feita no prprio cdigo dos testes.
TDD comumente confundido com TFD, pois ambas as tcnicas sugerem criar os testes antes da
implementao do sistema, no entanto, as diferenas so signicativas. Ao contrrio de TFD, TDD
sugere a criao dos testes e do sistema como apenas uma tarefa, ou, por outro lado, como duas tarefas
distintas que so realizadas paralelamente e sincronizadamente.
Alm disso, o ciclo de TDD muito menor do que o de TFD, pois escrever apenas um teste e um
pequeno trecho de cdigo por vez muito mais rpido do que pensar em vrias situaes de teste e
do sistema de uma vez. Exatamente por isso, TDD tambm no pode ser descrito como TFD com
Refatorao.
Ainda, TDD no apenas uma tcnica voltada para criao de sistemas com testes automatizados,
ela tambm utilizada para auxiliar a criao do design do sistema, por isso, certos autores preferem
a denominao Test-Driven Design. Isso pode ser resumido como um dos objetivos de TDD, que
escrever um cdigo limpo que funcione.
Caractersticas
Enquanto TAD e TFD apenas adicionam a criao de testes automatizados ao processo do desenvolvi-
mento dos sistemas de software, TDD muda completamente a forma tradicional de implementao de
sistemas. Por isso, existem diversos estudos e pesquisas que tentam provar empiricamente a eccia
155
dessa tcnica [92]. Dentre eles, existem os que obtm concluses neutras, contra e a favor de TDD. Por
causa das inmeras pesquisas realizadas, um estudo foi feito para tentar unir os resultados das pesquisas
para chegar a uma concluso nica, mas mesmo este estudo no obteve concluses signicativas [33].
Os aspectos mais questionveis so: se TDD aumenta a qualidade dos sistemas [147, 25, 94]; se
desenvolver com TDD produtivo [116]; e se TDD inuencia positivamente o design do sistema [75,
74].
TDD uma prtica que ajuda a controlar e garantir a qualidade do sistema. Ela sugere uma forma
de desenvolvimento simples e disciplinada, com forte nfase na preveno de erros e na criao de
sistemas bem modularizados. Dessa forma, se ela for aplicada corretamente, dicilmente no melhorar
a qualidade do produto nal. No obstante, o prprio ciclo de TDD propicia que os testes cubram uma
grande parcela do cdigo-fonte, j que s deve ser implementado o suciente para que os testes sejam
executados com sucesso. A alta cobertura dos testes no uma garantia de qualidade, mas um bom
indcio.
Em relao produtividade, difcil chegar a uma concluso, pois depende muito de cada pessoa.
O tipo e o nvel de experincia de um programador em relao a TDD e o desenvolvimento de software
inuencia na produtividade. Alm disso, existem questes ainda mais subjetivas, como as relacionadas
com os gostos e costumes de cada um. Pode ser difcil e at prejudicial mudar o comportamento de um
desenvolvedor que trabalha h dcadas de uma mesma forma.
Quanto ao design, o ciclo curto de passos denido por TDD cria uma dependncia forte entre o
cdigo do sistema e os testes, o que favorece e facilita a criao de sistemas com alta testabilidade.
um mito dizer que com TDD todo o design de um sistema emerge dos testes, mas eles ajudam signica-
tivamente a criao de parte dele. O mesmo ocorre com TFD, mas com TDD o ritmo de alterao do
design mais dinmico, j que a cada teste e refatorao podem surgir novas ideias.
Contudo, a elaborao prvia de uma arquitetura do sistema deve ser pensada e inicialmente seguida,
mas, a partir da, os casos de teste e as refatoraes guiam a criao e as alteraes no design do sistema.
O design inicial do sistema pode ser elaborado com a ajuda de DDD (Domain-Driven Design) [8]. Outra
maneira de pensar a respeito do design inicial das unidades com o auxlio dos testes de aceitao.
Quando estas estratgias so utilizadas, recomendado o uso de ferramentas apropriadas, como as que
seguem a linguagem de BDD, que ser descrito a seguir.
9.4 Desenvolvimento Dirigido por Comportamento (BDD)
Como foi descrito na seo anterior, TDD no apenas uma prtica de vericao do cdigo-fonte. Ela
uma prtica de desenvolvimento de software que ajuda a criao de um cdigo limpo que funcione e
que inuencie na elaborao do design. Apesar disso, TDD no deve ser utilizado como soluo nica
para criao de sistemas bem desenhados. imprescindvel um vasto conhecimento de programao,
tais como os principais conceitos de modularizao e de orientao a objetos.
Alm disso, um bom design de sistema aquele que representa adequadamente o contexto da apli-
cao e as necessidades do cliente. Para isso, fundamental o entendimento dos requisitos. Mtodos
geis recomendam que haja colaborao com o cliente, principalmente por meio da comunicao fre-
quente e efetiva (preferencialmente face a face).
Contudo, h uma grande ponte entre o entendimento dos requisitos e a implementao pertinente do
cdigo-fonte do sistema e dos testes. Este distanciamento pode ser reduzido com a ajuda dos testes de
aceitao e com a utilizao de uma linguagem nica e uente a todos os membros da equipe, incluindo
os clientes [104]. Uma das tendncias das ferramentas de testes automatizados tornar os testes cada vez
mais prximos de uma documentao do sistema com linguagem natural. Algumas ferramentas criam
DSLs (Domain Specic Languages) prprias para isso, como a Hamcrest; j outras geram documentos
legveis a partir do cdigo dos testes, como a TestDox.
156
Entretanto, mesmo com a ajuda destas ferramentas, no trivial criar uma linguagem ubqua entre
cliente e time de desenvolvimento. Tambm no certo que as histrias sero bem denidas e os
testes de aceitao sero bem implementados. A comunicao e a colaborao entre os envolvidos no
desenvolvimento de um sistema de software algo to subjetivo que pode ser impossvel determinar
todos os possveis problemas que podem ocorrer. Pensar atravs do comportamento de um sistema pode
ajudar a amenizar essas diculdades.
Desenvolvimento Dirigido por Comportamento (BDD de Behavior-Driven Development) uma
prtica de desenvolvimento identicada por Dan North em 2003 [34]. Ela recomenda o mesmo ciclo de
desenvolvimento de TDD, contudo, induzindo os participantes a utilizar uma linguagem diferente. Ao
invs de usar os termos tpicos de testes e vericaes como test suite, test case e assert das ferramentas
xUnit, as ferramentas de BDD induzem o uso de uma linguagem nica (ubqua) entre cliente e equipe
de desenvolvimento. Os termos utilizados por elas so comuns em descries de requisitos, tais como
specication, behavior, context e should.
No obstante, BDD integra explicitamente alguns princpios de DDD, de testes de aceitao e das
reas de qualidade de software para simplicar e sistematizar a denio das funcionalidades e dos
cenrios de teste. A denio das funcionalidades deve complementar o esqueleto denido na Figura
9.4. Este formato simples torna a descrio das funcionalidades especca, mensurvel, vivel, relevante
e estimvel. Um exemplo de histria pode ser visto na Figura 9.5.
1 Funcionalidade: ...
2 Como um(a) ...
3 Quero ...
4 Com o objetivo de ...
Figura 9.4: Esqueleto de histria sugerido por BDD.
1 Funcionalidade: Clculo total de imposto da empresa
2 Como um(a) contador(a)
3 Quero somar todos impostos da empresa dentro de um perodo
4 Com o objetivo de exibir os dados na internet para protestar contra o governo
Figura 9.5: Exemplo de histria no formato sugerido por BDD.
Os cenrios de teste tambm possuem um esqueleto predenido de passos, que o padro das
descries de teste de analistas de qualidade (Figura 9.6). Os passos Dado so semelhantes aos mtodos
de set up das ferramentas xUnit. Os passos Quando correspondem chamada da funcionalidade em
teste. Por ltimo, a chamada Ento anloga s vericaes. A Figura 9.7 possui um exemplo de
cenrio de teste para a histria da Figura 9.5.
A ferramenta JBehave (primeira ferramenta de BDD), que foi criada por Dan North, baseia-se na
leitura de arquivos de texto com histrias descritas no formato de passos, com uma linguagem prxima
a de pessoas que no possuem perl tcnico de computao. O cdigo dos testes (comportamentos)
carregam estes arquivos e os traduzem para fazer chamadas s funcionalidades em teste. Vale ressaltar
que os passos podem ser parametrizveis, o que facilita a reutilizao de cdigo-fonte dos testes.
A ideia do JBehave semelhante da ferramenta Fit
1
, criada por Ward Cunningham por volta de
2002. Aprincipal diferena que, enquanto Fit trabalha comarquivos contendo diversos tipos de tabelas,
JBehave trabalha com arquivos unicamente neste formato. Essas ferramentas podem ser tanto utilizadas
1
Framework for Integrated Testing.
157
1 Cenrio: ...
2 Dado ...
3 Quando ...
4 Ento...
5
6 # Cenrios mais complexos podem possuir passos extras concatenados com "E":
7 Cenrio: ...
8 Dado ...
9 E ...
10 Quando ...
11 Ento ...
12 E ...
Figura 9.6: Esqueleto de histria sugerido por BDD.
1 Cenrio: Calcular total de impostos sobre o faturamento anual
2 Dado que em 2009 a empresa faturou R$ 200.000,00 bruto
3 E o total de impostos chega a 40% do total bruto
4 Quando calculo o total de impostos da empresa em 2009
5 Ento a empresa gastou R$ 80.000,00 em impostos
6 E obteve rendimento lquido de apenas R$ 120.000,00
Figura 9.7: Exemplo de histria no formato sugerido por BDD.
para testes de unidade quanto para testes integrados (como os de aceitao). Como mostra a Figura 9.8,
pode-se escrever os testes com BDD seguindo o ciclo de ATDD, descrito na Seo 3.2.
Figura 9.8: Ciclo de ATDD.
Hoje, existe uma grande gama de ferramentas de BDD para diversas linguagens de programao,
todas seguindo a mesma abordagem. Dentre elas esto RSpec, Cucumber, JDave e BDoc.
158
9.5 Concluses
O nvel de testabilidade do sistema implica diretamente na qualidade dos testes automatizados. Mesmo
que o desenvolvedor conhea padres e boas prticas de automao, nem sempre ele conseguir colocar
emprtica seu conhecimento durante a implementao dos testes porque eles precisaro necessariamente
contornar as diculdades causadas pelo alto acoplamento dos mdulos e objetos da aplicao.
Quando os sistemas so implementados sem se preocupar com os possveis cenrios de testes, os
sistemas tendem a possuir uma baixa testabilidade, mesmo que eles tenham arquiteturas elegantes e
estejam bem implementados. Apesar de os sistemas com cdigo-fonte de qualidade serem mais fceis
de se testar, os testes automatizados precisam ter total controle do sistema em teste para que os casos de
testes sejam implementados com facilidade.
Por isso, altamente recomendvel utilizar as abordagens em que os testes so implementados antes
da implementao (TFD, TDD e BDD), pois elas foram os desenvolvedores a criarem cdigo mais
coeso, pouco acoplado e com alta testabilidade.
Entretanto, programas que possuem uma boa modelagem orientados a objetos tende a ter alta testa-
bilidade. Se cada classe tiver apenas uma responsabilidade e for possvel injetar suas dependncias,
ento a preparao dos testes tornam-se mais simples com o auxlio de Objetos Dubls (vide Seo 6.2)
e as vericaes necessrias cam fceis de serem elaboradas.
Para esses casos, o uso de TAD tambm se torna uma alternativa promissora. TAD s deve ser
evitada quando o custo da criao e da manuteno dos testes se torna mais alto do que a execuo
montona e repetida dos testes manuais.
159
160
Parte III
Gerenciamento de Testes Automatizados
161
Captulo 10
Mtricas
Mtrica uma relao de uma ou mais medidas para denir se um sistema, componente ou processo
possui um certo atributo. Uma medida uma avaliao em relao a um padro denido. Por exemplo,
para saber se um sistema grande (atributo) podemos utilizar a medida de linhas de cdigo, que tambm
podemos chamar de mtrica de linhas de cdigo [128, 127], j que toda medida representa um atributo,
e uma mtrica pode ser composta de uma nica medida.
Mtricas so fundamentais para reviso, planejamento e gerenciamento de projetos. Este captulo
discute seus benefcios e apresenta algumas mtricas que so pertinentes para acompanhar os testes
automatizados e a qualidade do sistema. Dentre elas esto Cobertura e Testabilidade, que podem ser
teis para muitos projetos, e outras que so mais bencas para projetos com certas caractersticas, tais
como projetos legados ou os que possuem equipes inexperientes em testes.
10.1 Mtricas para Testes Automatizados
Toda metodologia de desenvolvimento de software busca respeitar acordos e contratos denidos com
o cliente, sejam acordos de prazos, qualidade, escopo ou custo. O sucesso de um projeto depende de
organizao, planejamento, gerenciamento e aprendizado.
Organizao o princpio bsico para que todas as informaes estejam claras para facilitar o en-
tendimento do contexto e da situao corrente do projeto. reas de trabalho informativas [130] contendo
poucas informaes, mas que so muito relevantes, so mais valiosas do que documentos completos e
detalhados que acabam sendo deixados em segundo plano devido ao grande tempo que necessrio para
o estudo.
Planejamento uma proposta de organizao de tarefas para serem executadas [41]. A proposta
feita contendo previses do futuro, criadas, geralmente, com base na experincia obtida de trabalhos an-
teriores. Como nenhum planejamento e ningum consegue prever o futuro com exatido, indiscutvel
que todos eles esto sujeitos a enganos. O que pode ser feito tentar minimizar a quantidade de enganos.
Para isso, os mtodos geis recomendam trabalhar em iteraes curtas de desenvolvimento, pois mais
fcil prever o futuro prximo (uma ou duas semanas) do que prever o que ir acontecer a longo prazo
(meses ou anos).
J o gerenciamento feito pela observao do andamento do projeto e pela coordenao da equipe
para concentrar o trabalho nas tarefas mais prioritrias no momento da iterao. Para observar, com
xito, o andamento dos projetos, so necessrias informaes rpidas, claras, atualizadas e pertinentes
[41].
O aprendizado fundamental para o sucesso de trabalhos futuros devido experincia adquirida que
ajuda a evitar que erros sejam repetidos. Mtodos geis incentivam a criao de reunies de retrospec-
tivas depois do trmino de uma iterao de desenvolvimento para revisar e relembrar o andamento do
projeto, principalmente referente s diculdades encontradas [49]. Nessa reunio, devem ser reforados
163
os pontos que foram positivos e que precisam ser valorizados nas iteraes seguintes, assim como
previsto que sejam encontradas solues para aspectos que no foram satisfatrios e que precisam ser
melhorados [130].
As mtricas so fundamentais para qualquer metodologia alcanar o sucesso em um projeto, pois
elas so um artifcio bsico para reviso, planejamento e gerenciamento. Como diz Morris A. Cohen:
No podemos gerenciar o que no podemos medir [47]. Elas exercem um papel fundamental no
gerenciamento de tarefas e projetos, mas precisam ser bem organizadas e utilizadas nos momentos cor-
retos. Existem estudos e abordagens sistematizadas para coleta de mtricas e avaliao da qualidade de
sistemas [48].
As mtricas podem ser utilizadas apropriadamente quando houver necessidade de conhecer certos
aspectos de um trabalho para estabelecer objetivos, como sugere o processo PDCA
1
[134]. Em alguns
momentos, os problemas no esto claros ou visveis para a equipe, por isso a coleta de mtricas ajuda
a identicar pontos que devem ser melhorados. As curtas iteraes das metodologias geis seguem esta
estrutura.
Quando os objetivos j esto denidos, pode-se utilizar a abordagem GQM
2
[12], que sugere a
coleta unicamente das mtricas pertinentes para alcanar o objetivo denido. Isso evita um esforo
desnecessrio de coletar e interpretar outras mtricas menos importantes para o contexto. Portanto,
GQM til para ajudar a acompanhar as solues propostas dos problemas citados nas retrospectivas.
Uma das tarefas mais importantes do processo de desenvolvimento gerenciar a qualidade do
cdigo-fonte e do produto produzido, que uma tarefa complexa devido ao carter altamente subje-
tivo e pessoal da caracterstica. Para acompanhar a evoluo da qualidade, fundamental o emprego de
uma ou mais mtricas que consigam representar a qualidade para o contexto do projeto.
No caso das metodologias que integram testes automatizados como controle de qualidade, em es-
pecial a Programao eXtrema que recomenda TDD como uma de suas prticas primrias, comum a
coleta de mtricas de testes automatizados para o acompanhamento dos testes, da qualidade do cdigo-
fonte e do produto nal [11, 101]. Como os testes inuenciam diretamente a qualidade e o progresso
de um projeto, um bom conjunto de mtricas dos testes pode elucidar o estado e ajudar a corrigir o
andamento do projeto, estabelecer prioridades e estipular novas metas.
As sees seguintes apresentam algumas mtricas de testes automatizados e de qualidade que so
teis tanto para equipes que esto comeando a aprender e aplicar testes automatizados como para
aquelas j experientes que possuem grandes baterias de testes automatizados. Tambm so valiosas
tanto para projetos legados quanto para os recm-criados.
10.2 Cobertura
A mtrica de cobertura de cdigo indica quais pontos do sistema foram exercitados (executados ou
cobertos) pelos casos de teste [144]. Os pontos do sistema podem ser classes, mtodos, blocos e linhas,
sendo que a granularidade mais na de cobertura que geralmente as ferramentas obtm so as linhas de
cdigo executadas. A Figura 10.3 apresenta o relatrio de cobertura dos testes gerados com a ferramenta
Eclemma para Java aps a execuo dos testes da Figura 10.2 sob o cdigo da Figura 10.1 que possui
simplesmente um mtodo que calcula o mximo divisor comum (m.d.c.) de dois nmeros naturais.
As linhas em tons mais claros (verde: 7, 8, 10, 11, 12 e 17) foram executadas pelos testes, ao
contrrio das linhas com tom mais escuro (vermelho: 13, 14 e 15). As linhas que no foram executadas
indicam que faltam testes para o cenrio onde o resto da diviso entre dividendo e divisor diferente de
zero. O tom intermedirio (amarelo: 9) signica que a linha foi parcialmente executada, isto , algumas
das operaes foram realizadas e outras no. Neste caso, a cobertura aponta que faltam cenrios de teste
1
Plan -> Do -> Check -> Act ou Planeje -> Faa -> Estude -> Aja.
2
GQM: Goal -> Question -> Metrics ou Objetivo -> Questo -> Mtricas.
164
1 public class MathHelper {
2
3 // Algoritmo de Euclides: mdc(a, b) = mdc(b, resto(a, b)) => (a = q * b + r)
4 public static long mdc(long a, long b) {
5 long dividendo = Math.max(a, b);
6 long divisor = Math.min(a, b);
7 if(dividendo < 0 || divisor < 0) throw new IllegalArgumentException("ops");
8 if(dividendo == 0 || divisor == 0) return 0;
9 long resto = dividendo % divisor;
10 while(resto != 0) {
11 dividendo = divisor;
12 divisor = resto;
13 resto = dividendo % divisor;
14 }
15 return divisor;
16 }
17
18 }
Figura 10.1: Exemplo de cdigo para vericao da cobertura.
1 // referncias do JUnit
2 import static org.junit.Assert.assertEquals;
3 import org.junit.Test;
4
5 public class MathHelperTests {
6
7 @Test public void mdcComZeroEhZero() {
8 assertEquals(0, MathHelper.mdc(0, 1));
9 assertEquals(0, MathHelper.mdc(1, 0));
10 assertEquals(0, MathHelper.mdc(0, 0));
11 }
12
13 @Test public void mdcComUmEhUm() {
14 assertEquals(1, MathHelper.mdc(1, 1));
15 assertEquals(1, MathHelper.mdc(1, 7));
16 assertEquals(1, MathHelper.mdc(1, 20));
17 assertEquals(1, MathHelper.mdc(1, 25));
18 assertEquals(1, MathHelper.mdc(1, 100));
19 }
20
21 }
Figura 10.2: Exemplo de testes para vericao da cobertura.
165
Figura 10.3: Visualizao da cobertura do cdigo-fonte com a ferramenta Eclemma.
com dados de entrada invlidos, em que pelo menos um dos dados de entrada um nmero negativo.
Sendo assim, a condio if foi executada, enquanto o lanamento da exceo de erro no foi processada.
Note que um trecho exercitado no signica que ele est livre de defeitos ou que foi testado com-
pletamente [108]. Por exemplo, se a linha 11 possusse o cdigo incorreto long resto = 1, todos os
testes da Figura 10.2 continuariam sendo executados sem falhas. A cobertura nem sequer mostra se um
trecho est realmente sendo testado. Por exemplo, se o cdigo dos testes zesse apenas as chamadas do
mtodo mdc sem fazer as vericaes, isto , MathHelper.mdc(a, b), em vez de assertEquals(x,
MathHelper.mdc(a, b)), o resultado da cobertura continuaria sendo o mesmo.
A nica certeza que a mtrica de cobertura fornece que os trechos no exercitados no foram
testados. A falta de testes pode indicar trechos do sistema que so desnecessrios ou, ainda, pontos
especcos que podem conter falhas e que precisam ser vericados. Para exemplicar, qualquer erro
que no seja de compilao na linha 15 passa despercebido pela fraca bateria de testes apresentada.
Poderia ter desde erros de distrao, como, por exemplo, resto = dividendo / divisor, at um
erro grosseiro, como resto = 0.
Portanto, esta mtrica precisa ser interpretada com ateno e no deve ser utilizada como nico
indicador de qualidade do sistema, necessrio outras mtricas que complementam o conhecimento
adquirido da cobertura de testes.
Esta mtrica muito til para diversos contextos. Quando o sistema j possui uma bateria de testes
consideravelmente grande, ela fundamental para indicar novos pontos que precisam ser vericados.
Quando a bateria de testes pequena, no til para dar viso do sistema como um todo, mas pode ser
utilizada para encontrar novos pontos de vericao dentro de um pequeno mdulo do sistema.
Para os sistemas que no possuem testes automatizados, esta mtrica desnecessria porque o re-
sultado sempre ser 100% de trechos no cobertos. at possvel utilizar as ferramentas de coleta de
cobertura durante a realizao de testes manuais, mas os resultados obtidos s so teis para o instante
da execuo, isto , eles no so teis para serem acompanhados com o tempo porque as execues no
so elmente idnticas, o que torna invivel a interpretao coerente dos resultados.
Quando o sistema implementado com TDD ou TFD, a cobertura de cdigo tende a ser alta, j
que cada trecho de cdigo s deve ser adicionado aps um teste que o cubra [153]. Ento, ela pode
ser utilizada para auxiliar o desenvolvimento com testes a priori para indicar falhas no processo e para
vericar se um sistema possui indcios de ter sido escrito com TDD ou TFD.
166
10.3 Testabilidade
Testabilidade de software mede a facilidade da escrita de testes de qualidade dentro de um contexto
[142]. Testar um sistema ou uma funcionalidade nem sempre trivial, seja por meio de testes manuais
ou automatizados. Primeiramente, tem de ser possvel controlar o software, isto , execut-lo de acordo
com as necessidades do teste. Posteriormente, necessrio observar os efeitos colaterais causados por
uma determinada execuo que sero comparados com valores esperados.
Para que seja possvel controlar e observar um sistema apropriadamente necessrio que ele esteja
bem modularizado e isolado, pois, caso contrrio, os testes podem ser difceis ou at impossveis de
serem realizados. Testes complicados de serem criados ou mantidos tendem a ser pouco legveis e muito
suscetveis a erros. Tudo isso aumenta o custo-benefcio da automao de testes.
Testabilidade no uma mtrica intrnseca ao sistema, como total de linhas de cdigo, nmero de
classes etc. necessrio medir diversos fatores para ento calcular o grau de testabilidade de acordo com
alguma frmula matemtica baseada em determinaes subjetivas. Por isso, importante ter cuidado
ao interpretar esta mtrica, j que ela pode ser mais ou menos apropriada para um contexto especco.
Tambm preciso cautela ao comparar o grau de testabilidade entre sistemas que possuem contextos
diferentes.
A Figura 10.4 mostra um exemplo do grau de testabilidade de um mdulo do software Eclipse me-
dido com a ferramenta Testability-Explorer [69]. A ferramenta analisa o cdigo-fonte de cada classe
Java em busca de variveis de classe mutveis, incoerncias segundo a Lei de Demeter
3
[88] e classes
que no permitem injetar dependncias. As informaes coletadas so convertidas em um grau de testa-
bilidade que representa custo para se testar uma classe do sistema, sendo assim, quanto maior o custo,
pior.
Figura 10.4: Grau de testabilidade do mdulo Workbench do software Eclipse, medido com a ferramenta
Testability-Explorer.
As classes que possuem boa ou excelente testabilidade so mais fceis de testar automaticamente,
enquanto as que possuem baixa testabilidade (Needs Work) requerem mais ateno. Pode ser bem com-
plexo criar testes para as classes com baixa testabilidade, pois, geralmente, necessrio uma grande
quantidade de cdigo para preparar o ambiente de testes apropriadamente. Em algumas situaes, pode
at ser fcil de implementar os testes, mas provvel que eles contenham antipadres, tais como testes
lentos, intermitentes e frgeis.
3
A Lei de Demeter prope um estilo de design em que cada unidade deve ter conhecimentos limitados sobre outras
unidades. Uma unidade s deve trocar informaes com suas dependncias imediatas. Este princpio particularmente til
para criar sistemas orientados a objetos com baixo acoplamento.
167
10.3.1 Padres e Antipadres Inuenciam a Testabilidade
A testabilidade de um sistema est diretamente relacionada com o bom design [117, 97, 27, 78]. O grau
de testabilidade pode ser avaliado a partir de boas prticas de modularizao e de programao orientada
a objetos, que ajudam a tornar os sistemas mais simples e coesos, e, consequentemente, os testes cam
mais claros e fceis de implementar.
Um dos fatores mais comuns que afetam a testabilidade do sistema est relacionado com a im-
plementao do construtor de objetos. Objetos que so difceis de criar e de congurar dicultam a
criao de testes isolados. Existem inmeros antipadres no nomeados que prejudicam a testabilidade
de um sistema. A Figura 10.5 apresenta exemplo de construtores escritos em Java com alguns destes
antipadres. Para facilitar, os comentrios de cada exemplo esto nas prprias guras.
J a Figura 10.6 mostra objetos que so fceis de serem criados e isolados por meio do padro
Injeo de Dependncia [113]. Quando a criao dos objetos mais complexa, recomendado que o
processo de criao seja isolado em objetos prprios, atravs do uso de padres de projeto de criao,
tais como Builder, Factory e Prototype [61].
A denio das responsabilidades dos objetos e da forma como eles iro interagir determinam o
design de um sistema orientado a objetos. Isso uma das tarefas mais difceis e delicadas da orientao
a objetos, pois qualquer falha pode comprometer a manutenibilidade e a testabilidade. A Figura 10.7
apresenta um mtodo pouco coeso que viola a Lei de Demeter [88], portanto torna os testes difceis
de serem isolados. A Figura 10.8 apresenta como seria o mtodo refatorado para aumentar a coeso e
testabilidade do sistema.
Uma das boas prticas de orientao a objetos denir uma responsabilidade por classe. Esse padro
torna as classes fceis de serem implementadas, entendidas e testadas. Quando uma classe possui muitas
responsabilidades, o conjunto de casos de testes para test-la tende a aumentar consideravelmente, j que
as combinaes de dados de entrada podem aumentar fatorialmente. Alm disso, os testes tendem a ter
a legibilidade prejudicada. Quanto mais complexa for uma classe, maior sero os mtodos de set up e
mais vericaes so necessrias por casos de teste.
Existem vrios indcios que ajudam a identicar se uma classe est realizando mais tarefas do que
deveria. O primeiro deles se d pela facilidade de entendimento da classe. Outros indcios so os
atributos da classe raramente usados, que podem indicar que ela deve ser repartida entre outras menores.
Mais um indcio comum o uso de nomes de classes e variveis muito genricas, tais como manager,
context, environment, principal, container, runner etc. Devido ao carter extremamente abstrato dessas
classes, coerente que muitas responsabilidades sejam associadas a elas.
Ainda, a modelagem incorreta de heranas entre classes, por exemplo, que ferem o Princpio de Sub-
stituio de Liskov [89], podem tornar os testes difceis de serem modularizados e, consequentemente,
propiciar a replicao de cdigo-fonte (vide Seo 6.3.3).
Outro aspecto que prejudica a automao de testes so os estados globais mutveis, tais como var-
iveis globais e classes com o padro Singleton [61]. Estados globais so acessveis a vrios casos de
testes, portanto, os testes no cam completamente isolados. Isso signica que se um caso de teste
alterar um valor global, outros testes podero falhar.
Uma soluo que torna os testes mais complexos e lentos, mas que pode resolver o problema de
maneira padronizada, utilizar os mtodos de set up para denir o estado inicial das variveis globais
antes da execuo dos testes. Contudo, esta soluo requer que os testes sejam executados individual-
mente e sequencialmente, o que inviabiliza o uso de ferramentas de otimizao de testes que executam
paralelamente os casos de testes.
Esses padres e antipadres citados compem apenas uma pequena parcela das inmeras formas de
implementao que inuenciam a testabilidade do sistema. Ainda, existem diversos fatores prprios de
cada linguagem de programao que tambm podem facilitar ou dicultar os testes de software. O que
vlido para todas elas que denir e escrever os casos de testes antes da prpria implementao propicia
a criao de cdigo altamente testvel, j que o cdigo do sistema se adapta aos testes, e no o contrrio.
168
1 public class ObjetoComContrutorDeBaixaTestabilidade1 {
2 // No da para isolar (classe no tem mtodo setter)
3 private Dependencia dependencia = new Dependencia();
4 public ObjetoComContrutorDeBaixaTestabilidade1() {
5 }
6 }
7
8 public class ObjetoComContrutorDeBaixaTestabilidade2 {
9 private Dependencia dependencia;
10 public ObjetoComContrutorDeBaixaTestabilidade2() {
11 // No da para isolar (classe no tem mtodo setter)
12 dependencia = new Dependencia();
13 }
14 }
15
16 public class ObjetoComContrutorDeBaixaTestabilidade3 {
17 private Dependencia dependencia;
18 public ObjetoComContrutorDeBaixaTestabilidade3() {
19 // Arcabouos que usam reflexo para criao de objetos precisam do construtor
padro.
20 // Mas necessrio cuidados porque o objeto no est inicializado
21 // apropriadamente enquanto no forem injetadas as dependncias.
22 }
23 public void setDependencia(Dependencia dependencia) {
24 this.dependencia = dependencia;
25 }
26 }
27
28 public class ObjetoComContrutorDeBaixaTestabilidade4 {
29 public ObjetoComContrutorDeBaixaTestabilidade4() {
30 // Muita lgica no contrutor pode prejudicar a testabilidade.
31 // necessrio um trabalho adicional para criar o objeto apropriadamente.
32 // Use algum padro de projeto de criao de objetos.
33 if (x == 3) { ... }
34 for(int i = 0; i < n; i++) { ... }
35 while(true) { ... }
36 }
37 }
38
39 public class ObjetoComContrutorDeBaixaTestabilidade5 {
40 public ObjetoComContrutorDeBaixaTestabilidade5(Dependencia dependencia) {
41 // Testes no ficam isolados da classe DependenciaGlobal
42 DependenciaGlobal.metodoGlobal(dependencia);
43 }
44 }
Figura 10.5: Exemplo de implementao de construtores que tornam os objetos difceis de serem testa-
dos.
169
1 public class ObjetoComContrutorDeAltaTestabilidade1 {
2 private List list;
3 public ObjetoComContrutorDeAltaTestabilidade1() {
4 // Detalhes internos do objeto podem ser instanciados no contrutor
5 // lista geralmente uma exceo
6 list = new ArrayList();
7 }
8 }
9
10 public class ObjetoComContrutorDeAltaTestabilidade2 {
11 private Dependencia dependencia;
12 public ObjetoComContrutorDeAltaTestabilidade2(Dependencia dependencia) {
13 this.dependencia = dependencia; // Possvel injetar dependncia
14 }
15 }
Figura 10.6: Exemplo de implementao de construtores que tornam os objetos fceis de serem testados.
1 public class A {
2
3 public metodo(B b) {
4 // Violao da Lei de Demeter:
5 // Objeto A conhece toda hierarquia de classes do objeto B
6 // Difcil de isolar: A pergunta para B por informaes
7 var estado = b.getObjetoC().getObjetoD().getObjetoE().getEstado();
8 // ...
9 }
10 }
Figura 10.7: Exemplo de implementao de mtodos que so difceis de serem testados.
1 public class A {
2
3 // No pergunte, diga!
4 public metodo(E e) { // Possvel isolar dependncias
5 var estado = e.getEstado(); // A s conhece E
6 // Objetos B, C e D so dispensveis
7 // ...
8 }
9 }
Figura 10.8: Exemplo de implementao de mtodos que so fceis de serem testados.
170
Por isso recomendado o uso de TFD, TDD e BDD para criao de sistemas com alta testabilidade.
10.3.2 Quando Utilizar
Testabilidade pode ser til para analisar riscos e estabelecer quais pontos so mais crticos para se testar,
principalmente para equipes que esto comeando a aplicar testes automatizados em sistemas legados.
Contudo, ela tambm importante para acompanhar a qualidade dos testes automatizados, pois baixa
testabilidade (ou alto custo para se testar) implica testes com muitos antipadres.
Essa mtrica tambm auxilia na identicao dos mdulos do sistema em teste que precisam ser
refatorados, isto , mdulos que no possuem um bom design. Dessa forma, a anlise da testabilidade
do sistema, antes de adicionar novas funcionalidades, importante, pois ajuda a prevenir que uma nova
poro de cdigo seja inserida sobre uma arquitetura confusa, que pode tornar a implementao mais
complicada, alm de piorar o design do sistema.
TDD e TFD no s proporcionam alta cobertura dos testes, como tambm favorecem para que o
sistema seja testvel, pois a criao dos testes a priori implica que o cdigo do sistema se adapte aos
testes, e no o contrrio [16]. Portanto, testabilidade tambm til para acompanhar e vericar se o
desenvolvimento com TDD ou TFD est sendo feito apropriadamente.
10.4 Outras mtricas
As mtricas de cobertura e testabilidade podem ser teis para todos os contextos de desenvolvimento
de sistemas de software, mesmo quando estamos seguindo a abordagem PDCA ou GQM. No entanto,
existem muitas outras mtricas que so teis para contextos especcos e que podem ajudar a encontrar
defeitos, melhorar o cdigo do sistema e dos testes, alm serem mtricas valiosas para acompanhar a au-
tomao de testes, tanto para novos projetos quanto para projetos legados [144]. A seguir apresentada
uma lista com algumas destas mtricas:
1. Fator de Teste: o total de linhas dos testes pelo total de linhas do sistema. til para comparar
mdulos de um mesmo projeto e ajudar a determinar quais so os mdulos mais testados e quais
precisam de maior ateno. Esta mtrica no recomendada para o acompanhamento da evoluo
dos testes de um projeto, pois a lgica e o tamanho do cdigo dos testes no possuem qualquer
relao com a lgica do cdigo do sistema.
2. Nmero de testes por linha de cdigo do sistema: Esta mtrica pode ser til para acompanhar
a evoluo dos testes automatizados de um projeto, desde que o sistema cresa sem alteraes
drsticas quanto a sua complexidade.
3. Nmero de linhas de testes: Anlogo ao nmero de linhas de um sistema, esta mtrica d uma
pequena dimenso do cdigo dos testes e pode ser utilizada para o planejamento de tarefas de
estudo e manuteno do cdigo. A avaliao dessa mtrica em uma amostra do cdigo, como
classes ou mtodos, pode identicar testes que precisam de refatorao.
4. Nmero de testes: Mtrica para acompanhar a evoluo do desenvolvimento de testes. til,
principalmente, em projetos que esto comeando a ter testes automatizados.
5. Nmero de asseres: uma mtrica que ajuda a detectar se os testes esto realmente testando o
sistema, isto , se esto fazendo vericaes.
6. Nmero de testes pendentes: comum escrever testes marcados como pendentes (se o ar-
cabouo de teste fornecer essa funcionalidade) que sero implementados no momento apropriado.
Por exemplo, se o acompanhamento da quantidade dos testes ao longo de uma iterao indicar
171
que o nmero de pendncias no est diminuindo, pode ser um sinal de que os prazos esto curtos
e que os testes esto sendo sacricados.
7. Nmero de testes falhando: Esta mtrica til para detectar a fragilidade dos testes, alm de
servir de acompanhamento para o conserto dos mesmos.
8. Nmero de asseres por mtodo: Indica mtodos que talvez precisam ser refatorados caso o
nmero seja alto, pois podem ser responsveis por testar mais de uma ideia.
9. Replicao de cdigo dos testes: Identica trechos do cdigo dos testes que precisam ser refa-
torados, assim como pode indicar que o cdigo do sistema tambm possui replicao de cdigo.
10. Quantidade de defeitos encontrados: Pode indicar a qualidade do sistema e tambm a falta de
testes automatizados.
11. Tempo de execuo da bateria dos testes: Mtrica para determinar se o programa ou os casos
de testes possuem gargalos de desempenho que precisam ser otimizados ou refatorados.
10.5 Concluses
Os valores obtidos das mtricas esto diretamente relacionados ao contexto do projeto e do sistema,
pois elas dependem de muitos fatores como a linguagem de programao e a complexidade do produto.
Dessa forma, invivel utiliz-las isoladamente para denir o estado de um projeto ou mesmo para
comparar projetos distintos.
Para estes ns, sempre necessrio uma anlise que far a interpretao das informaes, prin-
cipalmente porque os resultados podem apresentar valores que no so esperados para a realidade do
projeto, isto porque as mtricas so, muitas vezes, facilmente burladas, propositalmente ou por falta de
experincia com o desenvolvimento dos testes, como demonstram os exemplos a seguir:
Exemplo 1: Um nmero alto de testes e de fator de teste pode aparentar que um sistema possui
poucos defeitos, j que passa a impresso de que foram vericados diferentes cenrios de testes,
mas tambm pode indicar que o cdigo do sistema possui uma grande replicao de cdigo, e,
portanto, os testes tambm so replicados.
Exemplo 2: Uma alta cobertura do cdigo provavelmente indica que o sistema est muito bem
testado, j que no h trechos de cdigo esquecidos, mas tambm pode mostrar que o sistema
foi muito mal testado, caso os testes no possuam vericaes, apenas chamadas dos mtodos do
sistema.
Estas mtricas relacionadas a testes automatizados ajudam a estabelecer objetivos de melhoria da
qualidade e da produtividade da automao dos testes, dentre outros objetivos que so comuns a diversos
projetos. A Tabela 10.1 aponta alguns destes objetivos e as mtricas mais recomendadas para ajudar no
gerenciamento.
importante lembrar que outras mtricas podem ser geradas a partir da combinao de uma ou mais
destas mtricas, pois cada uma delas utiliza uma medida diferente. A busca de novas mtricas sempre
til, pois quanto mais evidncias, mais fcil a anlise dos dados e tambm a denio de estratgias.
No entanto, a coleta de mtricas no deve prejudicar a agilidade do processo, assim como o excesso de
informaes no deve tirar o foco do que realmente necessrio melhorar.
172
X
X
X
X
X
X
X
X
X
X
X
Objetivo
Mtrica
1 2 3 4 5 6 7 8 9 10 11 12 13
Encontrar defeitos o o o
Melhorar o cdigo do sistema o o o o
Melhorar o cdigo dos testes o o o o o o o o o o
Introduzir testes automatizados em novos projetos o
Introduzir testes automatizados em sistemas legados o o
Acompanhar a automao dos testes o o o o o o o
Tabela 10.1: Objetivo vs. Mtrica (Goal vs. Metric). Legenda: (1) Testabilidade; (2) Cobertura; (3)
Fator de Teste; (4) Nmero de testes por linha de cdigo do sistema; (5) Nmero de linhas de testes;
(6) Nmero de testes; (7) Nmero de asseres; (8) Nmero de testes pendentes; (9) Nmero de testes
falhando; (10) Nmero de asseres por mtodo; (11) Replicao de cdigo dos testes; (12) Quantidade
de defeitos encontrados; e (13) Tempo de execuo da bateria dos testes.
173
174
Captulo 11
Consideraes Finais
Desenvolvimento de software uma tarefa complexa que exige conhecimento tcnico, organizao,
ateno, criatividade e tambm muita comunicao. previsvel que durante o desenvolvimento alguns
destes requisitos falhe, mas imprevisvel o momento que iro falhar. Por isso, imprescindvel que
exista uma maneira fcil e gil de executar todos os testes a qualquer momento, e isso vivel com o
auxlio de testes automatizados.
A automao dos testes traz segurana para fazer alteraes no cdigo, seja por manuteno, refa-
torao ou at mesmo para adio de novas funcionalidades. Alm disso, um teste programtico permite
criar testes mais elaborados e complexos, que podero ser repetidos identicamente inmeras vezes.
Ainda, a automao aumenta a quantidade de tempo gasto com a vericao do sistema e diminui
o tempo gasto com a identicao e correo de erros (tempo perdido). Todos os testes podem ser
executados a qualquer momento e, por consequncia, os erros tendem a ser encontrados mais cedo.
possvel at automatizar a execuo dos testes, com ferramentas que cam constantemente vericando
se um cdigo foi alterado ou com aquelas que obtm o cdigo de um repositrio automaticamente e
rodam a bateria de testes por meio de um script.
No entanto, a automao de testes um processo complexo, sujeito a erros e que precisa de
manuteno. Por isso, fundamental que as baterias de testes sejam de alta qualidade, ou seja, organi-
zadas, legveis, rpidas etc. Para isso, essencial o conhecimento de boas prticas, padres, antipadres,
e indcios de problemas.
Alm disso, os testes automatizados possuem inuncia na forma que um software modelado. Os
sistemas que so implementados sem testes automatizados tendem a possuir uma baixa testabilidade,
mesmo que o cdigo seja de alta qualidade. Por isso, aconselhvel utilizar abordagens que foram os
desenvolvedores a criarem cdigo com alta testabilidade.
Apesar dos testes automatizados ajudarem na criao de uma modelagem coesa e pouco acoplada
do sistema, o objetivo principal desta prtica vericar a qualidade de diferentes caractersticas que
so importantes para o projeto. Portanto, fundamental a utilizao das solues propostas pela rea
de Teste de Software, que so completamente compatveis com as abordagens sugeridas pela rea de
Metodologias geis. Por exemplo, possvel integrar boas prticas de vericao de cdigo emconjunto
com desenvolvimento dirigido por testes, alm de que o progresso da automao de testes de um projeto
pode ser acompanhado por meio de mtricas de software.
11.1 Pontos para Pesquisa
Esta rea de pesquisa est em crescimento, existem muitas pesquisas a serem feitas, muitas ferramentas
ainda no produzidas e muitas tcnicas ainda no evidenciadas. Testes automatizados j tm trazido
benefcios signicativos para muitos projetos, mas pesquisas que comparam as tcnicas de escrita ou
que comprovam a eccia dessa prtica ainda podem ser teis. Todavia, a tendncia facilitar a escrita
175
dos testes para baixar o custo de implementao e manuteno. Esta tendncia comprovada por ferra-
mentas com APIs mais fceis de usar, que geram cdigo de teste, e outras que at geram casos de testes
pertinentes.
Abaixo, segue uma lista de propostas de ferramentas para serem implementadas e de estudos empri-
cos que so difceis de serem realizados, j que complicado isolar outras variveis do desenvolvimento
de software que atrapalham a interpretao dos resultados.
Sugestes de pesquisas: Pesquisa em que quatro grupos pequenos com o mesmo nvel de ex-
perincia de programao iro implementar um mesmo sistema. Uma das equipes utilizar
TDD, outra TFD, outra TAD e outra ir fazer apenas testes manuais. O tempo de desenvolvi-
mento e a qualidade do cdigo e do produto gerado sero analisados para buscar evidncias
de vantagens e de desvantagens de cada prtica.
Pesquisa em que dois grupos com o mesmo nvel de experincia de programao e de TDD
iro implementar um mesmo sistema. Uma das equipes utilizar TDD e com o uso exaustivo
de Objetos Dubls, enquanto o segundo grupo ir fazer testes contendo certa integrao dos
mdulos e s utilizar Objetos Dubls para casos crticos. O tempo de desenvolvimento e a
qualidade do cdigo, dos testes e do produto gerado sero analisados para buscar evidncias
de vantagens e de desvantagens de cada prtica.
Pesquisa em que dois grupos com o mesmo nvel de experincia de programao e de TDD
iro implementar um mesmo sistema que tenha um linguajar no conhecido pelas equipes.
Uma das equipes utilizar TDD enquanto a outra, o BDD. O tempo de desenvolvimento e a
qualidade do cdigo, dos testes e do produto gerado sero analisados para buscar evidncias
de vantagens e de desvantagens de cada prtica.
Sugestes de Estudos: Continuar a documentar padres, antipadres e indcios de problemas.
Encontrar padres ao se testar os Padres de Projetos e os Arquiteturais.
Encontrar padres de testes para Programao Funcional.
Encontrar padres de testes de Web Services.
Sugestes de Ferramentas: Criar arcabouos para testes com aspectos. A partir de pointcuts,
prottipos de classes podem ser gerados automaticamente para a realizao dos testes.
Aperfeioar as ferramentas de relatrios de testes para torn-los mais legveis e terem maior
utilidade para documentao. Por exemplo, as ferramentas podem analisar os nomes dos
mtodos que utilizam a conveno camel case ou o caractere underline para formatar de
uma maneira mais legvel, com espao. J existem ferramentas com esse propsito, mas
ainda falta integrao com as ferramentas mais populares.
Complemento da ferramenta Python-QAssertions e converso da ferramenta para outras lin-
guagens. Outras asseres podem ser adicionadas, como uma que produza casos teis de
testes para expresses regulares, ou, ento, asseres que gerem testes teis para padres de
projetos e operaes comuns em banco de dados (CRUD).
Criar ferramentas que facilitem e incentivem o uso de padres identicados.
Criar frameworks de testes automatizados prprios para testarem sistemas paralelos e dis-
tribudos.
Sugestes de Ferramentas para Testes com Persistncia de Dados: Adaptar as ferramentas de
testes com persistncia de dados de arcabouos Web para que os testes sejam executados em
diversas instncias de banco de dados em memria, assim, os testes podero ser executados
em paralelo.
176
Sugestes de Ferramentas para Testes de Interface de Usurio: Ferramenta que gera uma Ca-
mada de Abstrao das Funcionalidades da interface de usurio para facilitar a escrita dos
testes de interface.
Aperfeioamento das ferramentas de gravao de testes de interface, de tal modo que facilite
a criao de mdulos, evitando a repetio de cdigo. Tambm pode-se evidenciar os pontos
que precisam ser refatorados.
Estudos e ferramentas para testes de usabilidade, baseadas em heursticas recomendadas pela
rea de Interao Homem-Computador. Para testes de interface Web, podem ser analisados
documentos CSS para identicar o contraste das cores dos componentes, assim como o
tamanho das fontes utilizadas.
Ferramentas para facilitar testes de leiaute. Elas podem detectar componentes que no esto
visveis assim como irregularidades do leiaute. Em aplicaes Web, podem ser analisados
os componentes que possuam a propriedade de invisibilidade (display), assim como as pro-
priedades de localizao tridimensional (z-index).
Sugestes de Ferramentas de Mtricas de Testes: Criar mtricas de padres de qualidade que
se baseiam padres do qualidade descritos na dissertao.
Criar ferramentas que detectem antipadres nos testes, assim como o Testability-Explorer
encontra antipadres de testabilidade no cdigo do sistema.
Ferramentas para coleta e exibio de mtricas de testes automatizados. Converter a ferra-
menta Testability-explorer (para Java) para outras linguagens.
Criar um rob que explore repositrios de cdigo e ferramentas de administrao de defeitos
para se obter mtricas que relacionam quantidade de defeitos com a quantidade de testes.
177
178
Apndices
179
Apndice A
Teste de Carga com JMeter
JMeter uma ferramenta livre para Desktop, implementada em Java/Swing e que facilita a criao de
testes de carga, estresse, desempenho e longevidade. Os testes so criados com auxlio da interface
de usurio, dispensando o uso de cdigo-fonte. O teste denido por intermdio de uma rvore de
comandos a ser executados (Plano de Teste), sendo que cada comando representado por um elemento
fornecido pela interface. As guras a seguir mostram um exemplo simples de um teste de carga para
uma aplicao Web.
Na Figura A.1 possvel ver as informaes globais do teste (lado direito da gura) e a rvore de
comandos a ser executados (lado esquerdo). No Plano de Teste, possvel denir inmeras variveis (a
tabela da gura), as quais so visveis a todos os comandos do teste. J em relao a rvore de elementos,
cada tipo de comando representado por um cone e um nome. Alguns desses comandos sero descritos
nas prximas guras.
Figura A.1: Conguraes do Plano de Teste com JMeter.
O comando Usurios do tipo Thread Group (Grupo de Threads, Figura A.2), que dene a quan-
181
tidade de usurios que sero simulados acessando o sistema (Number of Threads). Ainda, possvel
congurar algumas opes de como esses usurios iro fazer as requisies (as outras opes da parte
direita da janela). Por exemplo, a Loop Count dene quantas vezes cada usurio ir repetir os passos do
teste.
Note que os valores denidos nessa janela utilizam algumas das variveis denidas no comando
Plano de Teste. Isso foi feito para centralizar as conguraes mais importantes em um s lugar. Assim,
uma mesma rvore de comandos pode ser facilmente aproveitada para realizar os testes em diferentes
ambientes, bastando alterar as variveis pertinentes.
Figura A.2: Conguraes dos Usurios que sero simulados pelo JMeter.
Outro comando de congurao, que essencial para testes de aplicaes Web, o do tipo HTTP
Request Defaults (Figura A.3). Nele possvel denir o protocolo de acesso, o servidor e a porta,
assim como o tipo de codicao (encoding) e os tempos mximos de conexo e resposta (timeouts) das
requisies, alm de outras conguraes.
Tendo denido as conguraes centrais do teste, momento de denir quais pginas os usurios
simulados iro acessar, representado pelo comando Test. Esse comando serve para agrupar um conjunto
de aes para melhorar a organizao e reutilizao dos elementos. Existem ainda outros comandos
lgicos, tais como condicionais e de laos.
Nesse teste simples, o usurio ir apenas acessar a pgina inicial (Home, Figura A.4) e fazer uma
busca (Busca, Figura A.5), que so comandos do tipo HTTP Request (Requisio HTTP). Para acessar
a pgina inicial, basta a execuo de uma requisio HTTP do tipo GET no caminho /, enquanto, para
executar a busca, necessrio fazer um POST para /search contendo o texto a ser buscado.
O comando de requisio HTTP ainda possui outras opes, alm de que possvel sobrescrever
as conguraes denidas no comando HTTP Request Defaults. Para denir essas opes, preciso
conhecer em detalhes como funciona o sistema.
Quando o Plano de Teste executado, o JMeter carrega as conguraes, cria as threads que simu-
laro os usurios e executa as requisies previamente denidas. Entretanto, um teste no completa-
182
Figura A.3: Conguraes padres do servidor.
Figura A.4: Requisio HTTP GET na pgina inicial do sistema em teste.
183
Figura A.5: Requisio HTTP POST para realizar uma busca no sistema.
mente automatizado se ele no coleta e exibe as informaes pertinentes para anlise, por isso, o JMeter
tambm fornece diversos elementos que geram relatrios.
Por exemplo, a Figura A.6 mostra um grco onde possvel visualizar todos os tempos de resposta
das requisies feitas pelos usurios. A partir desses valores, so calculados a mdia, mediana, desvio
padro e, tambm, a vazo que representa a quantidade de requisies que so suportadas por minuto.
Esse exemplo bem simples, mas que pode ser bem til para avaliar a capacidade da infraestrutura
da sua aplicao. Para transform-lo em um teste de estresse, basta aumentar a quantidade de usurios e
de requisies at que o servidor caia ou o desempenho se torne insuportavelmente lento.
Os testes podem ser feitos para cada funcionalidade do sistema, ou, ento, pode-se criar uma sequn-
cia de passos que comum dos usurios fazerem. Apenas importante ressaltar que as funcionalidades
mais populares e mais pesadas do sistema devem ser priorizadas, ou seja, as que possuem mais risco de
derrubar os servidores.
184
Figura A.6: Um dos grcos que pode ser gerado pelo JMeter.
185
186
Apndice B
Biblioteca CUnit
Na Seo 6.4.12 h um exemplo de testes criados com o arcabouo CUNit, agora, a Figura B.1 apresenta
um esqueleto de como criar uma bateria (suite) de testes, ou seja, como cadastrar as funes que devem
ser executadas pelo arcabouo (linhas 13 a 33).
OCUnit fornece vrias maneiras de executar as baterias de testes. Omodo convencional a Interface
Automatizada (linhas 42 a 44), que executa os testes sem interveno humana e imprime os resultados
em um arquivo XML. A Interface Bsica (linhas 35 a 40) tambm inicia os testes automaticamente,
mas permite executar individualmente baterias ou testes. Quanto aos resultados, eles so impressos no
console, com quantidade de detalhes que pode ser congurada. Ainda h a Interface Interativa, a qual
permite que o usurio controle o uxo de execuo dos testes.
187
1 #include <stdio.h>
2 /* Referncias do CUnit */
3 #include <CUnit/CUnit.h>
4 #include <CUnit/Basic.h>
5 #include <CUnit/Automated.h>
6
7 void test_um(void) { /* ... */ }
8 void test_dois(void) { /* ... */ }
9 void test_tres(void) { /* ... */ }
10
11 /* Execuo dos testes com CUnit */
12 int main() {
13 CU_pSuite suite = NULL;
14
15 /* Inicializa registro de testes do CUnit */
16 if (CUE_SUCCESS != CU_initialize_registry())
17 return CU_get_error();
18
19 /* Adiciona a suite de testes ao registro */
20 suite = CU_add_suite("Suite", NULL, NULL);
21 if (NULL == suite) {
22 CU_cleanup_registry();
23 return CU_get_error();
24 }
25
26 /* Adiciona casos de testes suite de testes */
27 if ((NULL == CU_add_test(suite, "test_um", test_um)) ||
28 (NULL == CU_add_test(suite, "test_dois", test_dois)) ||
29 (NULL == CU_add_test(suite, "test_tres", test_tres))
30 ) {
31 CU_cleanup_registry();
32 return CU_get_error();
33 }
34
35 /* Executa todos os testes usando Interface Bsica */
36 CU_basic_set_mode(CU_BRM_VERBOSE);
37 CU_basic_run_tests();
38 printf("\n");
39 CU_basic_show_failures(CU_get_failure_list());
40 printf("\n\n");
41
42 /* Executa todos os testes usando Interface Automatizada */
43 CU_automated_run_tests();
44 CU_list_tests_to_file();
45
46 return CU_get_error();
47 }
Figura B.1: Biblioteca CUnit.
188
Referncias Bibliogrcas
[1] Gojko Adzic. Bridging the Communication Gap: Specication by Example and Agile Acceptance
Testing. Neuri Limited, 2009.
[2] Scott W. Ambler. Test driven database design. TASS Quarterly magazine, page 4, September
2006. Toronto Association of Systems and Software Quality.
[3] Scott W. Ambler and Ron Jeffries. Agile Modeling: Effective Practices for Extreme Programming
and the Unied Process. Wiley, 2002.
[4] Scott W. Ambler and Pramod J. Sadalage. Refactoring Databases: Evolutionary Database De-
sign. Addison-Wesley, 2006.
[5] Prasanth Anbalagan and Tao Xie. Apte: automated pointcut testing for aspectj programs. In
WTAOP 06: Proceedings of the 2nd workshop on Testing aspect-oriented programs, pages 27
32, New York, NY, USA, 2006. ACM.
[6] Ann Anderson, Ralph Beattie, Kent Beck, David Bryant, Marie DeArment, Martin Fowler, Mar-
garet Fronczak, Rich Garzaniti, Dennis Gore, Brian Hacker, Chet Hen-drickson, Ron Jeffries,
Doug Joppie, David Kim, Paul Kowalsky, Debbie Mueller, Tom Murasky, Richard Nutter, Adrian
Pantea, and Don Thomas. Chrysler goes to extremes. Distributed Computing, 1(10):2428, Oc-
tober 1998.
[7] Susan G. Archer, Laurel Allender, and Celine Richer. Software durability - is it important? can
it be achieved? In Proceedings of the Seventh International Conference on Human-Computer
Interaction, pages 593596, 1997.
[8] Abel Avram and Floyd Marinescu. Domain-Driven Design Quickly. Lulu.com, 2007.
[9] Alberto Avritzer and Elaine J. Weyuker. Generating test suites for software load testing. In
International Symposium on Software Testing and Analysis (ISSTA), pages 4457, 1994.
[10] Alberto Avritzer and Elaine J. Weyuker. The automatic generation of load test suites and the
assessment of the resulting software. IEEE Transactions on Software Engineering, 21(9):705
716, September 1995.
[11] Liane Ribeiro Pinto Bandeira. Metodologia baseada em mtricas de teste para indicao de testes
a serem melhorados. Dissertao eletrnica, Biblioteca Digital de Teses e Dissertaes da UFPE,
Setembro 2008.
[12] Vitor R. Basili, Gianluigi Caldiera, and H. Dieter Rombach. The goal question metric. In Ency-
clopedia of Software Engineering, pages 528532, 1996.
[13] Kent Beck. Simple smalltalk testing: With patterns. First Class Software, Inc., 1994.
189
[14] Kent Beck. Make it run, make it right: Design through refactoring. The Smalltalk Report, 6(4):19
24, January 1997.
[15] Kent Beck. Extreme Programming Explained: Embrace Change. Addison-Wesley, 1999.
[16] Kent Beck. Test-Driven Development: By Example. Addison-Wesley, 2002.
[17] Kent Beck and Cynthia Andres. Extreme Programming Explained: Embrace Change, 2nd Edi-
tion. Addison-Wesley, 2004.
[18] Kent Beck et al. Manifesto for Agile Software Development. Home page: http://
agilemanifesto.org, 2001.
[19] Kent Beck and Martin Fowler. Planning Extreme Programming. Addison-Wesley, 2001.
[20] Kent Beck and Mike Potel. Kent Becks Guide to Better Smalltalk. Cambridge University Press,
1998.
[21] Boris Beizer. Black-Box Testing: Techniques for Functional Testing of Software and Systems.
Wiley, 1995.
[22] Yochai Benkler. Coases Penguin, or Linux and the Nature of the Firm. Computing Research
Repository (CoRR), 2001.
[23] Yochai Benkler. The Wealth of Networks: How Social Production Transforms Markets and Free-
dom. Yale University Press, 2006.
[24] Mario Luca Bernardi and Giuseppe Antonio Di Lucca. Testing aspect oriented programs: an
approach based on the coverage of the interactions among advices and methods. In Quality of
Information and Communications Technology, 2007. QUATIC2007. 6th International Conference
on the, pages 6576. IEEE Computer Society, 2007.
[25] Thirumalesh Bhat and Nachiappan Nagappan. Evaluating the efcacy of test-driven development:
industrial case studies. In ISESE 06: Proceedings of the 2006 ACM/IEEE international sympo-
sium on Empirical software engineering, pages 356363, New York, NY, USA, 2006. ACM.
[26] Randolph Bias. Walkthroughs: Efcient collaborative testing. IEEE Software, 8(5):9495,
September 1991.
[27] Robert V. Binder. Design for testability in object-oriented systems. CACM: Communications of
the ACM, 37(9):87101, 1994.
[28] Robert V. Binder. Testing object-oriented systems: models, patterns, and tools. Addison-Wesley
Professional, 1999.
[29] Rex Black. Pragmatic Software Testing: Becoming an Effective and Efcient Test Professional.
Wiley, 2007.
[30] Joshua Bloch. Effective Java. Prentice Hall PTR, 2008.
[31] Barry W. Boehm. A spiral model of software development and enhancement. IEEE Computer,
pages 6172, May 1988.
[32] Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad, and Michael Stal. Pattern-
Oriented Software Architecture, Volume 1, A System of Patterns. Hardcover, 1996.
190
[33] Gerardo Canfora, Aniello Cimitile, Felix Garcia, Mario Piattini, and Corrado Aaron Visaggio.
Evaluating advantages of test driven development: a controlled experiment with professionals. In
ISESE 06: Proceedings of the 2006 ACM/IEEE international symposium on Empirical software
engineering, pages 364371, New York, NY, USA, 2006. ACM.
[34] David Chelimsky, Dave Astels, Bryan Helmkamp, Dan North, Zach Dennis, and Aslak Hellesoy.
The RSpec Book: Behaviour Driven Development with RSpec, Cucumber, and Friends. Pragmatic
Bookshelf, 2009.
[35] Paulo Cheque and Fabio Kon. Desenvolvendo com agilidade: Experincias na reimplementao
de um sistema de grande porte. In Primeiro Workshop de Desenvolvimento Rpido de Aplicaes
(WDRA), realizado em conjunto com o VI Simpsio Brasileiro de Qualidade de Software, 2007.
[36] Paulo Cheque and Fabio Kon. A importncia dos testes automatizados: Controle gil, rpido e
convel de qualidade. Engenharia de Software Magazine, 1(3):5457, 2008.
[37] Tony Clear. The waterfall is dead: long live the waterfall!! ACM SIGCSE (Special Interest Group
on Computer Science Education) Bulletin, 35(4):1314, 2003.
[38] Alistair Cockburn. Agile Software Development. Addison-Wesley Longman, 2002.
[39] Alistair Cockburn and Laurie Williams. The costs and benets of pair programming. In Proceed-
ings of the First International Conference on Extreme Programming and Flexible Processes in
Software Engineering (XP2000), Cagliari, Sardinia, Italy, June 2000.
[40] Mike Cohn. User Stories Applied: For Agile Software Development. Addison Wesley Longman
Publishing Co., Inc., Redwood City, CA, USA, 2004.
[41] Mike Cohn. Agile Estimating and Planning. Prentice Hall PTR, 2005.
[42] Microsoft Corporation. Engineering Software for Accessibility. Microsoft Press, 2009.
[43] Lisa Crispin and Janet Gregory. Agile Testing: A Practical Guide for Testers and Agile Teams
(Addison-Wesley Signature Series). Addison-Wesley, 2009.
[44] Lisa Crispin and Tip House. Testing Extreme Programming. Addison-Wesley, 2002.
[45] Philip B. Crosby. Quality Is Free. Mentor, 1980.
[46] Alexandre Freire da Silva. Reexes sobre o ensino de metodologias geis na academia, na
indstria e no governo. Masters thesis, Departamento de Cincia da Computao, Instituto de
Matemtica e Estatstica - Universidade de So Paulo, Setembro 2007.
[47] Thomas H. Davenport and Jeanne G. Harris. Competing on Analytics: The New Science of Win-
ning. Harvard Business School Press, 2007.
[48] Vieri del Bianco, Luigi Lavazza, Sandro Morasca, Davide Taibi, and Davide Tosi. The qualispo
approach to oss product quality evaluation. In FLOSS 10: Proceedings of the 3rd International
Workshop on Emerging Trends in Free/Libre/Open Source Software Research and Development,
pages 2328, New York, NY, USA, 2010. ACM.
[49] Esther Derby and Diana Larsen. Agile Retrospectives: Making Good Teams Great. Pragmatic
Bookshelf, 2006.
[50] Edsger W. Dijkstra. The humble programmer. CACM: Communications of the ACM, 15, 1972.
191
[51] M. E. Drummond, Jr. A perspective on system performance evaluation. IBM Systems Journal,
8(4):252263, 1969.
[52] Paul Duvall, Steve Matyas, and Andrew Glover. Continuous Integration: Improving Software
Quality and Reducing Risk. Addison-Wesley Professional, 2007.
[53] Eduardo Martins Guerra. Um estudo sobre refatorao de cdigo de teste. Masters thesis, Insti-
tuto Tcnolgico de Aeronutica, 2005.
[54] Gerald D. Everett and Raymond McLeod Jr. Software Testing. John Wiley and Sons, Inc, 2007.
[55] Michael Feathers. Working Effectively with Legacy Code. Prentice Hall, 2008.
[56] Mark Fewster and Dorothy Graham. Software Test Automation. Addison-Wesley Professional,
1999.
[57] Dairton Luiz Bassi Filho. Experincias com desenvolvimento gil. Masters thesis, Departamento
de Cincia da Computao, Instituto de Matemtica e Estatstica - Universidade de So Paulo,
Maro 2008.
[58] Ira R. Forman and Nate Forman. Java Reection in Action. Manning Publications, 2004.
[59] Martin Fowler. Refactoring: Improving the Design of Existing Code. Addison-Wesley, 1999.
[60] Steve Freeman and Nat Pryce. Growing Object-Oriented Software, Guided by Tests. Addison-
Wesley Professional, 2009.
[61] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns Elements of
Reusable Object-Oriented Software. Professional Computing Series. Addison-Wesley, 1995.
[62] David Gelperin and Bill Hetzel. The growth of software testing. CACM: Communications of the
ACM, 31(6):687695, 1988.
[63] Tom Gilb and Dorothy Graham. Software Inspection. Addison Wesley, 1993.
[64] Robert L. Glass. Persistent software errors. IEEE Transactions on Software Engineering,
7(2):162168, March 1981.
[65] Robert L. Glass. The standish report: does it really describe a software crisis? 49(8):1516, 2006.
[66] The Standish Group. The CHAOS report, 1994.
[67] The Standish Group. The CHAOS report, 2003.
[68] Atul Gupta and Pankaj Jalote. Test inspected unit or inspect unit tested code? In Empirical
Software Engineering and Measurement (ESEM), pages 5160. IEEE Computer Society, 2007.
[69] Misko Hevery. Testability explorer: using byte-code analysis to engineer lasting social changes
in an organizations software development process. In OOPSLA Companion 08: Companion
to the 23rd ACM SIGPLAN conference on Object-oriented programming systems languages and
applications, pages 747748, New York, NY, USA, 2008. ACM.
[70] Dorota Huizinqa and Adam Kolawa. Automated Defect Prevention: Best Practices in Software
Management. Wiley-IEEE Computer Society Press, 2007.
[71] Andy Hunt and Dave Thomas. Pragmatic Unit Testing in Java with JUnit. The Pragmatic Pro-
grammers, 2003.
192
[72] The IEEE. IEEE standard for software reviews and audits. ANSI/IEEE STD 1028-1988, IEEE
Computer Society, 1988.
[73] Melody Y. Ivory and Marti A Hearst. The state of the art in automating usability evaluation of
user interfaces. ACM Comput. Surv., 33(4):470516, 2001.
[74] David Janzen. Software architecture improvement through test-driven development. In Ralph E.
Johnson and Richard P. Gabriel, editors, OOPSLA Companion, pages 240241. ACM, 2005.
[75] David S. Janzen and Hossein Saiedian. On the inuence of test-driven development on software
design. In CSEET 06: Proceedings of the 19th Conference on Software Engineering Education
& Training, pages 141148, Washington, DC, USA, 2006. IEEE Computer Society.
[76] Cem Kaner. Improving the maintainability of automated test suites. Proceedings of the Tenth
International Quality Week, 1997.
[77] Cem Kaner, Jack Falk, and Hung Q. Nguyen. Testing Computer Software. Wiley, 1999.
[78] R. A. Khan and K. Mustafa. Metric based testability model for object oriented design (mtmood).
SIGSOFT Softw. Eng. Notes, 34(2):16, 2009.
[79] Joshua Kierievsky. Refactoring to Patterns. Addison-Wesley Professional, 2001.
[80] Taeksu Kim, Chanjin Park, and Chisu Wu. Mock object models for test driven development.
In Software Engineering Research, Management and Applications, 2006. Fourth International
Conference on, pages 221228. IEEE Computer Society, 2006.
[81] Donald Knuth. Structured programming with go to statements. ACM Journal Computing Surveys,
6(4), 1974.
[82] Lasse Koskela. Test Driven: Practical TDD and Acceptance TDD for Java Developers. Manning
Publications, 2007.
[83] Mohit kumar, Akashdeep sharma, and Sushil Garg. A study of aspect oriented testing techniques.
In Industrial Electronics & Applications, 2009. ISIEA 2009. IEEE Symposium on, pages 996
1001. IEEE Computer Society, 2009.
[84] Craig Larman and Victor R. Basili. Iterative and incremental development: a brief history. IEEE
Computer, pages 4756, July 2003.
[85] Otvio Augusto Lazzarini Lemos, Fabiano Cutigi Ferrari, Paulo Cesar Masiero, and
Cristina Videira Lopes. Testing aspect-oriented programming pointcut descriptors. In Roger T.
Alexander, Stephan Herrmann, and Dehla Sokenou, editors, Workshop on Testing Aspect-
Oriented Programs (WTAOP), pages 3338. ACM, 2006.
[86] Otvio A. L. Lemos, Jos Carlos Maldonado, and Paulo Cesar Masiero. Teste de unidades de
programas orientados a aspectos. In Simpsio Brasileiro de Engenharia de Software, 2004.
[87] Karl R. P. H. Leung and Wing Lok Yeung. Generating user acceptance test plans from test cases.
In COMPSAC, pages 737742. IEEE Computer Society, 2007.
[88] K. J. Lienberherr. Formulations and benets of the law of demeter. 1989.
[89] Barbara Liskov. Keynote address - data abstraction and hierarchy. In OOPSLA 87: Adden-
dum to the proceedings on Object-oriented programming systems, languages and applications
(Addendum), pages 1734, New York, NY, USA, 1987. ACM.
193
[90] Henry H. Liu. Software Performance and Scalability: A Quantitative Approach (Quantitative
Software Engineering Series). Wiley, 2009.
[91] Cristina Videira Lopes and Trung Chi Ngo. Unit-testing aspectual behavior. In In proc. of Work-
shop on Testing Aspect-Oriented Programs (WTAOP), held in conjunction with the 4th Interna-
tional Conference on Aspect-Oriented Software Development (AOSD05), 2005.
[92] Kim Man Lui and Keith C.C. Chan. Test-driven development and software process improvement
in china. In Proceedings of the 5th International Conference on eXtreme Programming and Ag-
ile Processes in Software Engineering (XP 2004), volume 3092 of Lecture Notes on Computer
Science, pages 219222, 2004.
[93] Tim Mackinnon, Steve Freeman, and Philip Craig. Endo-testing: unit testing with mock objects,
pages 287301. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 2001.
[94] Lech Madeyski. Test-Driven Development: An Empirical Evaluation of Agile Practice. Springer,
2009.
[95] Jos Carlos Maldonado, Mrcio Eduardo Delamaro, and Mario Jino. Introduo ao Teste de
Software. Campus, 2007.
[96] Robert C. Martin. The test bus imperative: Architectures that support automated acceptance
testing. IEEE Software, 22(4):6567, 2005.
[97] Robert C. Martin. Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall
PTR, 2008.
[98] Deborah J. Mayhew. The Usability Engineering Lifecycle: A Practitioners Handbook for User
Interface Design (Interactive Technologies). Morgan Kaufmann, 1999.
[99] Gerard Meszaros. XUnit Test Patterns: Refactoring Test Code. Addison-Wesley, 2007.
[100] Bertrand Meyer. Object-Oriented Software Construction, Second Edition. The Object-Oriented
Series. Prentice-Hall, Englewood Cliffs (NJ), USA, 1997.
[101] James B. Michael, Bernard J. Bossuyt, and Byron B. Snyder. Metrics for measuring the effective-
ness of software-testing tools. In International Symposium on Software Reliability Engineering
(ISSRE), pages 117128. IEEE Computer Society, 2002.
[102] Rodrigo M. L. M. Moreira, Ana C. R. Paiva, and Ademar Aguiar. Testing aspect-oriented pro-
grams. In Information Systems and Technologies (CISTI), 2010 5th Iberian Conference on, pages
16. IEEE Computer Society, 2010.
[103] Tomer Moscovich and John F. Hughes. Indirect mappings of multi-touch input using one and two
hands. pages 12751284. ACM, 2008.
[104] Rick Mugridge and Ward Cunningham. Fit for Developing Software: Framework for Integrated
Tests. Prentice Hall, 2005.
[105] Glenford J. Myers. The Art of Software Testing. John Wiley and Sons, New York, 1979.
[106] Syed Asad Ali Naqvi, Shaukat Ali, and M. Uzair Khan. An evaluation of aspect oriented testing
techniques. In Emerging Technologies, 2005. Proceedings of the IEEE Symposium on, pages
461466. IEEE Computer Society, 2005.
[107] NIST. National institute of standards and technology, 2002.
194
[108] H. Ohba. Software quality = test accuracy * test coverage. In International Conference on
Software Engineering (ICSE), pages 287295, 1982.
[109] Taiichi Ohno. Toyota Production System: Beyond Large-Scale Production. Productivity Press,
1998.
[110] William F. Opdyke. Refactoring Object-Oriented Frameworks. Ph.D. thesis, University of Illi-
nois, 1992.
[111] Behrooz Parhami. Defect, fault, error,..., or failure? In Reliability, IEEE Transactions on, vol-
ume 46, pages 450451. IEEE Reliability Society, 1997.
[112] Mary Poppendieck and Tom Poppendieck. Lean Software Development: An Agile Toolkit.
Addison-Wesley Professional, 2003.
[113] Dhanji Prasanna. Dependency Injection. Manning Publications, 2009.
[114] IEEE Press. Standard 610.12. IEEE standard glossary of software engineering terminology, 1990.
[115] Roger Pressman. Software Engineering: A Practitioners Approach. McGraw-Hill Science/Engi-
neering/Math, 2009.
[116] Viera K. Proulx. Test-driven design for introductory oo programming. In SIGCSE 09: Proceed-
ings of the 40th ACM technical symposium on Computer science education, pages 138142, New
York, NY, USA, 2009. ACM.
[117] Nat Pryce. Growing Object-Oriented Software, Guided by Tests. Addison-Wesley Professional,
2009.
[118] Vaclav Rajlich. Changing the paradigm of software engineering. Communications of the ACM,
49(8):6770, August 2006.
[119] Reginaldo R, Otvio Augusto Lazzarini Lemos, and Paulo Cesar Masiero. Minimizing stub
creation during integration test of aspect-oriented programs. In WTAOP 07: Proceedings of the
3rd workshop on Testing aspect-oriented programs, pages 16, New York, NY, USA, 2007. ACM.
[120] Stuart Reid. The art of software testing, second edition. glenford J. myers. Softw. Test, Verif.
Reliab, 15(2):136137, 2005.
[121] Andr Restivo and Ademar Aguiar. Towards detecting and solving aspect conicts and interfer-
ences using unit tests. In SPLAT 07: Proceedings of the 5th workshop on Software engineering
properties of languages and aspect technologies, page 7, New York, NY, USA, 2007. ACM.
[122] Don Roberts, John Brant, and Ralph E. Johnson. A refactoring tool for Smalltalk. Theory and
Practice of Object Systems (TAPOS), 3(4):253263, 1997.
[123] Winston W. Royce. Managing the development of large software systems: concepts and tech-
niques. In ICSE 87: Proceedings of the 9th international conference on Software Engineering,
pages 328338, Los Alamitos, CA, USA, 1987. IEEE Computer Society Press.
[124] David Saff and Michael D. Ernst. Can continuous testing speed software development? In
Fourteenth International Symposium on Software Reliability Engineering (ISSRE), pages 281
292, 2003.
[125] Goutam Kumar Saha. Understanding software testing concepts. Ubiquity, 2008(1):1, February
2008.
195
[126] Joc Sanders and Eugene Curran. Software Quality. Addison-Wesley, 1994.
[127] Danilo Sato, Alfredo Goldman, and Fabio Kon. Tracking the Evolution of Object Oriented Qual-
ity Metrics. In Proceedings of the 8th International Conference on Extreme Programming and
Agile Processes in Software Engineering (XP2007), pages 8492, 2007.
[128] Danilo Toshiaki Sato. Uso ecaz de mtricas em mtodos geis de desenvolvimento de software.
Masters thesis, Departamento de Cincia da Computao, Instituto de Matemtica e Estatstica -
Universidade de So Paulo, Agosto 2007.
[129] Ulrich Schoettmer and Toshiyuki Minami. Challenging the high performance high cost
paradigm in test. In International Test Conference (ITC 95), pages 870879, Altoona, Pa.,
USA, October 1995. IEEE Computer Society Press.
[130] Ken Schwaber. Agile Project Management with Scrum. Microsoft Press, 2004.
[131] Ken Schwaber and Mike Beedle. Agile Software Development with SCRUM. Prentice Hall, 2001.
[132] Mike Potel Sean Cotter. Inside Taligent Technology. Taligent Press, 1995.
[133] Helen Sharp, Yvonne Rogers, and Jenny Preece. Interaction Design: Beyond Human-Computer
Interaction. Wiley, 2007.
[134] Walter Andrew Shewhart. Statistical method from the viewpoint of quality control. In Dover
Publications, 1939.
[135] Sandro Silvestre. Desenvolvimento de software robusto. PhD thesis, Instituto de Pesquisas Tec-
nolgicas do Estado de So Paulo - Universidade de So Paulo, Dezembro 2006.
[136] Diomidis Spinellis. Code Quality: The Open Source Perspective. Addison-Wesley Professional,
2006.
[137] Susan H. Strauss and Robert G. Ebenau. Software Inspection Process. McGraw-Hill, 1994.
[138] Dave Thomas and Andy Hunt. Mock objects. In Software, IEEE, volume 19, pages 2224. IEEE
Computer Society, 2002.
[139] Jenifer Tidwell. Designing Interfaces. OReilly Media, 2005.
[140] James E. Tomayko. A comparison of pair programming to inspections for software defect reduc-
tion. Computer Science Education, 12(3):213222, 2002.
[141] Richard Torkar. Towards automated software testing - techniques, classications and frameworks.
Masters thesis, School of Engineering - Dept. of Systems and Software Engineering/Blekinge
Institute of Technology, 2006.
[142] K. Vahidi and A. Orailoglu. Testability metrics for synthesis of self-testable designs and effective
test plans. In VTS 95: Proceedings of the 13th IEEE VLSI Test Symposium, page 170, Washing-
ton, DC, USA, 1995. IEEE Computer Society.
[143] Arie van Deursen, Leon M. F. Moonen, Alexander van den Bergh, and Gerard Kok. Refactoring
test code. Preprint, Centrum voor Wiskunde en Informatica, department Software Engineering
(SEN), 2001.
[144] Auri Marcelo Rizzo Vincenzi, Jos Carlos Maldonado, Eric W. Wong, and Mrcio Eduardo Dela-
maro. Coverage testing of java programs and components. Sci. Comput. Program., 56(1-2):211
230, 2005.
196
[145] James A. Whittaker and Mike Andrews. How to break Web software: functional and security
testing of Web applications and Web services. Addison-Wesley, 2006.
[146] Laurie Williams and Robert Kessler. Pair Programming Illuminated. Addison-Wesley, 2002.
[147] Laurie A. Williams, E. Michael Maximilien, and Mladen A. Vouk. Test-driven development as a
defect-reduction practice. In ISSRE, pages 3448. IEEE Computer Society, 2003.
[148] Stephen Withall. Software Requirement Patterns (Best Practices). Microsoft Press, 2007.
[149] Yuk Kuen Wong. Modern Software Review: Techniques and Technologies. IRM Press, 2006.
[150] Tao Xie and Jianjun Zhao. Perspectives on automated testing of aspect-oriented programs. In
WTAOP 07: Proceedings of the 3rd workshop on Testing aspect-oriented programs, pages 712,
New York, NY, USA, 2007. ACM.
[151] Edward Yourdon. Structured Walkthrough. Prentice-Hall, 4th edition, 1989.
[152] Chuan Zhao and Roger T. Alexander. Testing aspect-oriented programs as object-oriented pro-
grams. In WTAOP 07: Proceedings of the 3rd workshop on Testing aspect-oriented programs,
pages 2327, New York, NY, USA, 2007. ACM.
[153] Hong Zhu, Patrick A. V. Hall, and John H. R. May. Software unit test coverage and adequacy.
CSURV: Computing Surveys, 29, 1997.
197