Title: Exemplo de desenvolvimento com testes
1Exemplo de desenvolvimento com testes
Prof. Dr. Alfredo Goldman Departamento de
Ciência da Computação IME / USP 3 de Abril de
2003 VI Semana da Computação
2Problema com testes
- Todos sabem devem ser escritos
- Poucos o fazem, e por quê não ?
- Estou com muita pressa
- Mas, isto cria um círculo vicioso
menos produtividade menos estabilidade
menos testes
mais pressão
3Como quebrar este ciclo
- Criando um ambiente simples de testes
- Depois de fazer os primeiros testes
- o hábito vem para ficar
- Vamos mostrar como seria um desenvolvimento
ideal.... - Através do JUnit...
- Mas, antes uma visão geral
4JUnit - como funciona ?
- Arcabouço Java para testes de unidade
- API para construir testes
- Classes básicas Test, TestCase, TestSuite,...
- Usam-se métodos tipo premissa
- assertTrue(), assertEquals, fail(), ...
- API para a execução de testes (TestRunners)
- Modo texto
- Modo gráfico
5Junit - origens e uso
- Criado por K. Beck e E. Gamma
- Padrão para testes em Java
- Permite a execução automática de testes
- Executa os testes de forma silenciosa
- Dirigido a testes de unidade
- Métodos
- Pode-se agrupar diversos testes
6Exemplo de uso
- Padrão use o método testXxx() para testar o
método xxx() - Utilize os métodos da classe TestCase
- assertEquals( objeto1, objeto2)
- assertTrue( varBool)
- assertNotNull( objeto)
- fail()
7JUnit na prática
- 1) O TestRunner recebe uma subclasse de TestCase
- 2) Descobre seus métodos (reflexão)
- 3) Para cada testXxx() cria nova instância
- garante independência !!
- executa setUp() testXxx() tearDown()
- 4) Possibilidades
- término OK, falha, ou exceção
8Criando uma seqüência de testes
- Classe TestSuite
- Método addTest adiciona um teste a lista
- Encontra os testes em uma classe (reflexão)
- new TestSuite( ClasseDeTestes.class)
- Pode-se juntar tudo
- TestSuite ts new TestSuite(tudo)
- ts.addTest( pacote.Teste1.testeX)
- ts.addTest( ClasseDeTestes.suite())
9Premissas em Java (JDK 1.4)
- Nova palavra chave assert
- assert (n gt 0) n não é positivo
- Podem ser desligadas facilmente
- Provocam um AssertionError quando falham
- Para usá-las
- javac -source 1.4 Classe.java
- java -ea Classe
10JUnit e premissas
- Premissas são usadas dentro do código
- Os testes JUnit ficam em classes separadas
- Não tem acesso a partes encapsuladas
- JUnit testa a partir da interface
- Premissas podem verificar lógica interna
11Testes de desempenho
- JUnitPerf
- Métodos para medir desempenho e escalabilidade
- TimedTest
- Mede, e limita o tempo do teste
- LoadTest
- Execução concorrente, configuração por timers
- ThreadTest
- Executa o teste como uma thread separada
12Testes de Stress
- JMeter - testa nos limites
- De carga
- Para diferentes tipos BDs, páginas WEB, objetos
Java - Gera gráficos
- Pode ser usado em conjunto com o JUnitPerf
13Testes de páginas WEB
- Testar do ponto de vista do usuário
- Através de páginas
- Testes funcionais
- Extensões do Junit
- HttpUnit e ServletUnit
- JXweb (especifica os testes em XML)
- XMLUnit
- Todos são projetos sourceforge
14JUnit na prática O programa
- Um sistema para representar diversas moedas
- Para começar algo simples.
15class Money private int fAmount
private String fCurrency public Money(int
amount, String currency) fAmount
amount fCurrency currency
public int amount() return fAmount
public String currency() return
fCurrency
16Para somar dois Moneys da mesma moeda
(currency) public Money add(Money m)
return new Money(amount()m.amount(),
currency())
17Questão de hábito
- Code a little, test a little, code a little, test
a little.... - Já temos um objeto, vamos testá-lo !!
- No JUnit os testes devem ser subclasses de
TestCase
18public class MoneyTest extends TestCase
// public void testSimpleAdd()
Money m12CHF new Money(12, "CHF") //
(1) Money m14CHF new Money(14, "CHF")
Money expected new Money(26,
"CHF") Money result m12CHF.add(m14CHF)
// (2) Assert.assertTrue(expecte
d.equals(result)) // (3) O
testSimpleAdd() consiste em Código para criar
os objetos Código para usar os objetos Código
para verificar os resultados. Falta fazer a
sobrecarga de equals
19Mas antes um teste para o equals
public void testEquals() Money m12CHF new
Money(12, "CHF") Money m14CHF new Money(14,
"CHF") Assert.assertTrue(!m12CHF.equals(null
)) Assert.assertEquals(m12CHF, m12CHF)
Assert.assertEquals(m12CHF,
new Money(12, "CHF")) // (1)
Assert.assertTrue(!m12CHF.equals(m14CHF)) //
lembrete o equals do object volta true se os //
objetos comparados são o mesmo.
20Agora sim public boolean equals(Object
anObject) if (anObject instanceof Money)
Money aMoney (Money) anObject
return aMoney.currency().equals(currency())
amount() aMoney.amount()
return false // faltou sobrecarregar o método
hashCode...
21Mas, já apesar dos testes serem pequenos já há
código duplicado... public class MoneyTest
extends TestCase private Money f12CHF
private Money f14CHF protected void
setUp() f12CHF new Money(12, "CHF")
f14CHF new Money(14, "CHF")
22Agora os testes podem ser rescritos
como public void testEquals()
assert(!f12CHF.equals(null))
assertEquals(f12CHF, f12CHF)
assertEquals(f12CHF, new Money(12, "CHF"))
assert(!f12CHF.equals(f14CHF)) public void
testSimpleAdd() Money expected new
Money(26, "CHF") Money result
f12CHF.add(f14CHF) assert(expected.equals(res
ult))
23Próximos passos
- Definir como rodar um teste individual
- Definir como rodar uma seqüência de testes.
24// forma estática, com classe interior TestCase
test new MoneyTest("simple add") public
void runTest() testSimpleAdd()
// forma dinâmica, usa reflexão TestCase test
new MoneyTest("testSimpleAdd")
25Pode-se automatizar testes
- Criando uma seqüência de testes
public static Test suite() TestSuite suite
new TestSuite() suite.addTest(new
MoneyTest("testEquals")) suite.addTest(new
MoneyTest("testSimpleAdd")) return suite
26Pode-se automatizar testes
public static Test suite() return new
TestSuite(MoneyTest.class)
Agora, um pouco de JUnit na prática.
27Continuando o projeto
Deve-se poder guardar diversos tipos de moeda
class MoneyBag private Vector fMoneis new
Vector() MoneyBag(Money m1, Money m2)
appendMoney(m1) appendMoney(m2)
MoneyBag(Money bag) for (int
i 0 i lt bag.length i)
appendMoney(bagi)
28Para os testes deve se criar também objetos do
novo tipo
protected void setUp() f12CHF new
Money(12, "CHF") f14CHF new Money(14,
"CHF") f7USD new Money( 7, "USD")
f21USD new Money(21, "USD") fMB1 new
MoneyBag(f12CHF, f7USD) fMB2 new
MoneyBag(f14CHF, f21USD)
29Devem se criar novos testes, mas os testes
antigos continuam lá
public void testBagEquals()
assert(!fMB1.equals(null))
assertEquals(fMB1, fMB1) assert(!fMB1.equals(
f12CHF)) assert(!f12CHF.equals(fMB1))
assert(!fMB1.equals(fMB2))
E devem continuar funcionando...
30Agora podemos melhorar o método add
public Money add(Money m) if
(m.currency().equals(currency()) ) return
new Money(amount()
m.amount(), currency()) return new
MoneyBag(this, m) // ops MoneyBag ! Money....
Agora existem duas representações de dinheiro...
interface IMoney public abstract IMoney
add(IMoney aMoney) //
31Mas, ainda não temos testes para tipos mistos...
- public void testMixedSimpleAdd()
- // 12 CHF 7 USD 12 CHF7 USD
- Money bag f12CHF, f7USD
- MoneyBag expected new MoneyBag(bag)
- assertEquals(expected, f12CHF.add(f7USD))
-
- Os outros testes seguem o mesmo padrão
- testBagSimpleAdd - soma MoneyBag com Money
- testSimpleBagAdd - soma Money com MoneyBag
- testBagBagAdd - soma dois MoneyBags
32Mais testes estão disponíveis
public static Test suite() TestSuite suite
new TestSuite() suite.addTest(new
MoneyTest("testMoneyEquals"))
suite.addTest(new MoneyTest("testBagEquals"))
suite.addTest(new MoneyTest("testSimpleAdd"))
suite.addTest(new MoneyTest("testMixedSimpleAdd"
)) suite.addTest(new MoneyTest("testBagSimple
Add")) suite.addTest(new MoneyTest("testSimpl
eBagAdd")) suite.addTest(new
MoneyTest("testBagBagAdd")) return suite
Agora sim vamos implementá-los...
33class Money implements IMoney public IMoney
add(IMoney m) return m.addMoney(this)
// class MoneyBag implements IMoney
public IMoney MoneyBag.add(IMoney m)
return m.addMoneyBag(this)
// // IMoney addMoney(Money aMoney)
IMoney addMoneyBag(MoneyBag aMoneyBag)
34Em Money. public IMoney addMoney(Money m)
if (m.currency().equals(currency()))
return new Money(amount()m.amount(),
currency()) return new
MoneyBag(this, m) public IMoney
addMoneyBag(MoneyBag s) return
s.addMoney(this)
35Em MoneyBag. public IMoney addMoney(Money m)
return new MoneyBag(m, this) public
IMoney addMoneyBag(MoneyBag s) return new
MoneyBag(s, this)
Surge um problema.... E se retira-se 12CHF de um
MoneyBag com 12CHF ???
36Primeiro o teste... public void testSimplify()
// 12 CHF7 USD -12 CHF 7
USD Money expected new Money(7, "USD")
assertEquals(expected, fMS1.add(new
Money(-12, "CHF")))
37Depois o código. public IMoney addMoney(Money m)
return (new MoneyBag(m, this)).simplify()
public IMoney addMoneyBag(MoneyBag s)
return (new MoneyBag(s, this)).simplify() priv
ate IMoney simplify() if (fMonies.size()
1) return (IMoney)fMonies.firstElement()
return this
38Desenvolvimento com testes
- Testes devem ser escritos assim que possível
- Testes devem ser adaptados segundo as mudanças
- Deixe os testes antigos rodando
- Quando surgem novas idéias (simplify), crie
testes, veja se funcionam, e se necessário altere
o código.