Exemplo 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
03/04/2003
VI Semana da
Computação
Problema 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 testes
menos produtividade
menos estabilidade
mais pressão
03/04/2003
VI Semana da
Computação
Como 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:
03/04/2003
VI Semana da
Computação
JUnit - 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
03/04/2003
VI Semana da
Computação
Junit - 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
03/04/2003
VI Semana da
Computação
Exemplo 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();
03/04/2003
VI Semana da
Computação
JUnit 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
03/04/2003
VI Semana da
Computação
Criando 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());
03/04/2003
VI Semana da
Computação
Premissas em Java (JDK 1.4)
Nova palavra chave: assert
assert (n > 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
03/04/2003
VI Semana da
Computação
JUnit 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
03/04/2003
VI Semana da
Computação
Testes 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
03/04/2003
VI Semana da
Computação
Testes 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
03/04/2003
VI Semana da
Computação
Testes 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
03/04/2003
VI Semana da
Computação
JUnit na prática: O programa
Um sistema para representar diversas
moedas;
Para começar: algo simples.
class 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;
}
}
03/04/2003
VI Semana da
Computação
Para somar dois “Moneys” da mesma moeda (currency):
public Money add(Money m) {
return new Money(amount()+m.amount(),
currency());
}
03/04/2003
VI Semana da
Computação
Questã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
public 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(expected.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
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.
Mas antes um teste para o equals
03/04/2003
VI Semana da
Computação
Agora 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...
03/04/2003
VI Semana da
Computação
Mas, 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");
}
}
03/04/2003
VI Semana da
Computação
Agora 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(result));
}
03/04/2003
VI Semana da
Computação
Próximos passos
Definir como rodar um teste individual;
Definir como rodar uma seqüência de
testes.
// 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");
03/04/2003
VI Semana da
Computação
Pode-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;
}
03/04/2003
VI Semana da
Computação
Pode-se automatizar testes
Ou apenas:
public static Test suite() {
return new TestSuite(MoneyTest.class);
}
Agora, um pouco de JUnit na prática.
03/04/2003
VI Semana da
Computação
Continuando 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 < bag.length; i++)
appendMoney(bag[i]);
}
}
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);
}
Para os testes deve se criar também objetos do novo tipo
public void testBagEquals() {
assert(!fMB1.equals(null));
assertEquals(fMB1, fMB1);
assert(!fMB1.equals(f12CHF));
assert(!f12CHF.equals(fMB1));
assert(!fMB1.equals(fMB2));
}
Devem se criar novos testes, mas os testes antigos continuam lá
E devem continuar funcionando...
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 podemos melhorar o método add
Agora existem duas representações de dinheiro...
interface IMoney {
public abstract IMoney add(IMoney aMoney);
//…
}
Mas, ainda não temos testes para tipos mistos...
public void testMixedSimpleAdd() {
// [12 CHF] + [7 USD] == {[12 CHF][7 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
Mais 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("testBagSimpleAdd"));
suite.addTest(new MoneyTest("testSimpleBagAdd"));
suite.addTest(new MoneyTest("testBagBagAdd"));
return suite;
}
Agora sim vamos implementá-los...
class 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);
}
Em 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);
}
Em 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 ???
Primeiro o teste...
public void testSimplify() {
// {[12 CHF][7 USD]} + [-12 CHF] == [7 USD]
Money expected= new Money(7, "USD");
assertEquals(expected,
fMS1.add(new Money(-12, "CHF")));
}
Depois 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();
}
private IMoney simplify() {
if (fMonies.size() == 1)
return (IMoney)fMonies.firstElement()
return this;
}
03/04/2003
VI Semana da
Computação
Desenvolvimento 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.

Ferramentas testes2003

  • 1.
    Exemplo de desenvolvimento comtestes Prof. Dr. Alfredo Goldman Departamento de Ciência da Computação IME / USP 3 de Abril de 2003 VI Semana da Computação
  • 2.
    03/04/2003 VI Semana da Computação Problemacom 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 testes menos produtividade menos estabilidade mais pressão
  • 3.
    03/04/2003 VI Semana da Computação Comoquebrar 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:
  • 4.
    03/04/2003 VI Semana da Computação JUnit- 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
  • 5.
    03/04/2003 VI Semana da Computação Junit- 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
  • 6.
    03/04/2003 VI Semana da Computação Exemplode 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();
  • 7.
    03/04/2003 VI Semana da Computação JUnitna 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
  • 8.
    03/04/2003 VI Semana da Computação Criandouma 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());
  • 9.
    03/04/2003 VI Semana da Computação Premissasem Java (JDK 1.4) Nova palavra chave: assert assert (n > 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
  • 10.
    03/04/2003 VI Semana da Computação JUnite 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
  • 11.
    03/04/2003 VI Semana da Computação Testesde 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
  • 12.
    03/04/2003 VI Semana da Computação Testesde 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
  • 13.
    03/04/2003 VI Semana da Computação Testesde 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
  • 14.
    03/04/2003 VI Semana da Computação JUnitna prática: O programa Um sistema para representar diversas moedas; Para começar: algo simples.
  • 15.
    class Money { privateint fAmount; private String fCurrency; public Money(int amount, String currency) { fAmount = amount; fCurrency = currency; } public int amount() { return fAmount; } public String currency() { return fCurrency; } }
  • 16.
    03/04/2003 VI Semana da Computação Parasomar dois “Moneys” da mesma moeda (currency): public Money add(Money m) { return new Money(amount()+m.amount(), currency()); }
  • 17.
    03/04/2003 VI Semana da Computação Questãode 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
  • 18.
    public class MoneyTestextends 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(expected.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
  • 19.
    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. Mas antes um teste para o equals
  • 20.
    03/04/2003 VI Semana da Computação Agorasim 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...
  • 21.
    03/04/2003 VI Semana da Computação Mas,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"); } }
  • 22.
    03/04/2003 VI Semana da Computação Agoraos 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(result)); }
  • 23.
    03/04/2003 VI Semana da Computação Próximospassos 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");
  • 25.
    03/04/2003 VI Semana da Computação Pode-seautomatizar 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; }
  • 26.
    03/04/2003 VI Semana da Computação Pode-seautomatizar testes Ou apenas: public static Test suite() { return new TestSuite(MoneyTest.class); } Agora, um pouco de JUnit na prática.
  • 27.
    03/04/2003 VI Semana da Computação Continuandoo 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 < bag.length; i++) appendMoney(bag[i]); } }
  • 28.
    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); } Para os testes deve se criar também objetos do novo tipo
  • 29.
    public void testBagEquals(){ assert(!fMB1.equals(null)); assertEquals(fMB1, fMB1); assert(!fMB1.equals(f12CHF)); assert(!f12CHF.equals(fMB1)); assert(!fMB1.equals(fMB2)); } Devem se criar novos testes, mas os testes antigos continuam lá E devem continuar funcionando...
  • 30.
    public Money add(Moneym) { if (m.currency().equals(currency()) ) return new Money(amount() + m.amount(), currency()); return new MoneyBag(this, m); } // ops MoneyBag != Money.... Agora podemos melhorar o método add Agora existem duas representações de dinheiro... interface IMoney { public abstract IMoney add(IMoney aMoney); //… }
  • 31.
    Mas, ainda nãotemos testes para tipos mistos... public void testMixedSimpleAdd() { // [12 CHF] + [7 USD] == {[12 CHF][7 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
  • 32.
    Mais testes estãodisponí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("testBagSimpleAdd")); suite.addTest(new MoneyTest("testSimpleBagAdd")); suite.addTest(new MoneyTest("testBagBagAdd")); return suite; } Agora sim vamos implementá-los...
  • 33.
    class Money implementsIMoney { 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); }
  • 34.
    Em Money. public IMoneyaddMoney(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); }
  • 35.
    Em MoneyBag. public IMoneyaddMoney(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 ???
  • 36.
    Primeiro o teste... publicvoid testSimplify() { // {[12 CHF][7 USD]} + [-12 CHF] == [7 USD] Money expected= new Money(7, "USD"); assertEquals(expected, fMS1.add(new Money(-12, "CHF"))); }
  • 37.
    Depois o código. publicIMoney addMoney(Money m) { return (new MoneyBag(m, this)).simplify(); } public IMoney addMoneyBag(MoneyBag s) { return (new MoneyBag(s, this)).simplify(); } private IMoney simplify() { if (fMonies.size() == 1) return (IMoney)fMonies.firstElement() return this; }
  • 38.
    03/04/2003 VI Semana da Computação Desenvolvimentocom 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.