Orientação a Objeto - Testes de Unidade

Objetivos da seção

Um exemplo: Tratamento de valores monetários no pacote p1.aplic.banco

Por que não usar double para valores financeiros?

public class Grana1 {
  public static void main(String[] args) {
    double x, y;
    x = 10000000000.0;
    y = 0.01;
    for(int i = 0; i < 10; i++) {
        System.out.println(x+y);
        x *= 10;
    }
  }
}
1.000000000001E10
1.0000000000001E11
1.00000000000001E12
1.000000000000001E13
1.0000000000000002E14
1.0E15
1.0E16
1.0E17
1.0E18
1.0E19
public class Grana2 {
  public static void main(String[] args) {
    double x = 1.00;
    for(int i = 0; i < 10; i++) {
        System.out.println(x);
        // aplica 10% de juros
        x += x * 0.1;
    }
  }
}
1.0
1.1
1.2100000000000002
1.3310000000000002
1.4641000000000002
1.61051
1.7715610000000002
1.9487171
2.1435888100000002
2.357947691

Uma classe para representar valores financeiros

Os testes de unidade

Uso do framework de testes JUNIT

Testes de unidade da classe Real2

package p1.aplic.bancotestes;

import junit.framework.*;
import p1.aplic.banco.*;

/**
 * Testes da classe Real2.
 *
 */
public class TestaReal2 extends TestCase {
  protected Real2 zero;
  protected Real2 zero2;
  protected Real2 quaseZero;
  protected Real2 umCentavo;
  protected Real2 menosUmCentavo;
  protected Real2 menosUmEVinteETres;

  public TestaReal2(String name) {
    super(name);
  }
  protected void setUp() {
    zero = new Real2();
    zero2 = new Real2(0.0);
    quaseZero = new Real2(0.004999);
    umCentavo = new Real2(0.01);
    menosUmCentavo = new Real2(-0.01);
    menosUmEVinteETres = new Real2(-1.23);
  }
  public void testEquals() {
    assertTrue(!zero.equals(null));
    assertEquals(zero, zero);
    assertEquals(zero, zero2);
    assertEquals(new Real2(0.01), umCentavo);
    assertTrue(!zero.equals(umCentavo));
    assertEquals(zero, quaseZero);
  }
  public void testCompareTo() {
    assertEquals("1", 0, zero.compareTo(zero));
    assertEquals("2", 0, zero.compareTo(new Real2()));
    assertEquals("3", 0, zero.compareTo(zero2));
    assertTrue("4", zero.compareTo(umCentavo) < 0);
    assertTrue("5", zero.compareTo(menosUmCentavo) > 0);
    assertEquals("6", 0, zero.compareTo(quaseZero));
  }
  public void testGetValor() {
    assertEquals("1", 0.0, zero.getValor(), 0.0);
    assertEquals("2", 0.0, zero2.getValor(), 0.0);
    assertEquals("3", 0.0, quaseZero.getValor(), 0.0);
    assertEquals("4", 0.01, umCentavo.getValor(), 0.0);
    assertEquals("5", -0.01, menosUmCentavo.getValor(), 0.0);
  }
  public void testToString() {
    assertEquals("1", "R$0,00", zero.toString());
    assertEquals("2", "R$0,00", quaseZero.toString());
    assertEquals("3", "R$0,01", umCentavo.toString());
    assertEquals("4", "R$-1,23", menosUmEVinteETres.toString());
    assertEquals("5", "R$-0,01", menosUmCentavo.toString());
  }
  public void testSetValor() {
    Real2 z1 = new Real2();
    z1.setValor(0.004999);
    assertEquals("1", 0.0, z1.getValor(), 0.0);
    z1.setValor(0.01);
    assertEquals("2", 0.01, z1.getValor(), 0.0);
    z1.setValor(-0.01);
    assertEquals("3", -0.01, z1.getValor(), 0.0);
  }
}

wpe1.jpg (29153 bytes)

    assertTrue("5", zero.compareTo(menosUmCentavo) > 0);
Failure: testGetValor(p1.aplic.bancotestes.TestaReal2):5 expected:<-0.01> but was:<0.0>
  public void setValor(double valor) {
    // Vai perder frações de centavos aqui
    // Ajustar 0.5 centavos é para arredondar ao
    // centavo mais próximo
    if(valor >= 0) {
      this.centavos = (long)(valor*100.0 + 0.5);
    } else {
      this.centavos = (long)(valor*100.0 - 0.5);
    }
  }
Failure: testGetValor(p1.aplic.bancotestes.TestaReal2):4 expected:<R$-1,23> but was:<R$-1,0-22>
  public String toString() {
    String resultado = "R$";
    long centavos = getCentavos();
    if(centavos < 0) {
      resultado += "-";
      centavos = -centavos;
    }
    long cent = centavos % 100;
    resultado += centavos/100 + "," + (cent < 10 ? "0" : "") + cent;
    return resultado;
  }

Uma solução mais genérica

/*
 * Desenvolvido para a disciplina Programacao 1
 * Curso de Bacharelado em Ciência da Computação
 * Departamento de Sistemas e Computação
 * Universidade Federal da Paraíba
 *
 * Copyright (C) 1999 Universidade Federal da Paraíba.
 * Não redistribuir sem permissão.
 */
package p1.aplic.banco;
import java.io.*;

/**
 * Classe abstrata representando uma moeda genérica com centavos.
 * O motivo da existência dessa classe é de permitir a manipulação
 * de valores financeiros sem se preocupar com frações de centavos.
 * Classes clientes podem usar double para manipular valores mas,
 * ao passar tais valores para uma Moeda, as frações de centavos
 * somem.
 * <P>A classe é abstrata porque não sabemos o nome da moeda e não temos portanto
 * um método toString().
 *
 * @author Jacques Philippe Sauvé, jacques@dsc.ufpb.br
 * @version 1.1 (adicionei setCentavos)
 * <br>
 * Copyright (C) 1999, 2000 Universidade Federal da Paraíba.
 */

public abstract class Moeda implements Serializable {
  private long  centavos;

  /**
   * Controi um valor 0,0 como Moeda.
   */
  public Moeda() {
    this(0.0);
  }

  /**
   * Controi um valor como Moeda.
   * @param valor O valor a representar.
   */
  public Moeda(double valor) {
    setValor(valor);
  }

  /**
   * Recupera o valor como double.
   */
  public double getValor() {
    return centavos / 100.0;
  }

  /**
   * Ajusta o valor a ser representado como moeda.
   * @param valor O valor a representar como moeda.
   */
  public void setValor(double valor) {
    // Vai perder frações de centavos aqui
    // Ajustar 0.5 centavos é para arredondar ao
    // centavo mais próximo
    if(valor >= 0) {
      this.centavos = (long)(valor*100.0 + 0.5);
    } else {
      this.centavos = (long)(valor*100.0 - 0.5);
    }
  }

  /**
   * Compara igualdade de duas moedas.
   * @param moeda O outro valor a comparar.
   */
  public boolean equals(Object outroObjeto) {
    if(!(outroObjeto instanceof Moeda)) {
      return false;
    }
    return getValor() == ((Moeda)outroObjeto).getValor();
  }

  /**
   * Compara dois valores de moeda.
   * @param outra A outra moeda a comparar.
   * @return 0 se a moeda for igual à outra moeda; -1 se ela for menor e +1 se for maior.
   */
   public int compareTo(Moeda outra) {
     long diferença = getCentavos() - outra.getCentavos();
     return diferença == 0 ? 0 : (diferença < 0 ? -1 : 1);
   }

  /**
   * Representa o valor da moeda como string.
   */
  public abstract String toString();

  protected long getCentavos() {
    return centavos;
  }
  protected void setCentavos(long centavos) {
    this.centavos = centavos;
  }
}
/*
 * Desenvolvido para a disciplina Programacao 1
 * Curso de Bacharelado em Ciência da Computação
 * Departamento de Sistemas e Computação
 * Universidade Federal da Paraíba
 *
 * Copyright (C) 1999 Universidade Federal da Paraíba.
 * Não redistribuir sem permissão.
 */
package p1.aplic.banco;
import java.io.*;

/**
 * A moeda Real (Brasil). O importante aqui é o método toString que
 * sabe o símbolo da moeda (R$).
 *
 * @author   Jacques Philippe Sauvé, jacques@dsc.ufpb.br
 * @version 1.1
 * <br>
 * Copyright (C) 1999 Universidade Federal da Paraíba.
 */

public class Real extends Moeda implements Serializable {

  /**
   * Controi um valor 0.0 em moeda Real
   */
  public Real() {
    this(0.0);
  }

  /**
   * Controi um valor em moeda Real.
   * @param valor O valor em reais.
   */
  public Real(double valor) {
    super(valor);
  }

  /**
   * Representa o valor da moeda como string
   */
  public String toString() {
    String resultado = "R$";
    long centavos = getCentavos();
    if(centavos < 0) {
      resultado += "-";
      centavos = -centavos;
    }
    long cent = centavos % 100;
    resultado += centavos/100 + "," + (cent < 10 ? "0" : "") + cent;
    return resultado;
  }
}

O que Testar?

Tipos de Testes

Algumas palavras de Beck e Gamma

oo-7 programa anterior próxima