Versão ALPHA! Este artigo está em versão 'Alpha' e, portanto, não foi ainda revisado corretamente

Teste

Introdução

Gen são classes que tem exclusivamente a responsabilidade de gerar algo. Por exemplo, dado um objeto do tipo X, gere um do tipo Y. Para que isso ocorra, na implementação dessa classe pode ser necessário delegar responsabilidades para outras classes e uso de funções para auxiliar a implementação desse Gen.

Para padronizar a nomenclatura de classes que chamamos de Gen, seguimos como convenção nomear essas classes com a palavra Gen no final do nome da classe, como por exemplo: dado um objeto do tipo Aluno, gere um objeto do tipo RelatorioDeAluno a partir do mesmo, então chamaríamos essa classe de RelatarioDeAlunoGen.

Passo-a-passo

Como elaborar o teste de um Gen

Antes de começar a elaborar o teste, é importante verificar se existe um construtor para o tipo de objeto que queremos gerar, assim podemos antecipar se realmente conseguiremos gerar um objeto na implementação do nosso Gen.

Basicamente todos o teste terão a seguinte estrutura:

  • dados de entrada - o cenário que iremos construir.
  • chamar o Gen - a ação que executará o que queremos testar.
  • validar a resposta - onde é validado o que aconteceu, como esperávamos ou não que ocorresse.

O Gen será invocado para gerar algo a partir de um ou mais parâmetros fornecidos, como mostra o exemplo abaixo. Nessa parte do teste também podemos aprimorar e ajustar nosso cenário de teste, como por exemplo explicitando os valores que iremos validar nas assertivas ou criando novos objectos, como veremos nos exemplos abaixo.

Aluno aluno = AlunosFalso.ALUNO_1;

No exemplo temos a classe AlunosFalso, onde definimos um determinado número de alunos e esses estão persistidos no banco de dados também para que de fato o teste possa contar com dados de teste razoáveis.

Se o nosso Gen chamasse RelatorioDeAlunoGen, então o invocariamos da seguinte forma:

RelatorioDeAluno relatorio = gen.gerar(aluno);

Para melhorar a compreensão, RelatorioDeAluno tem a seguinte estrutura:

public interface RelatorioDeAluno {

  interface Construtor extends br.com.objectos.comuns.base.Construtor<RelatorioDeAluno> {

    String getNomeDoAluno();

    List<Fatura> getFaturasEmAberto();

    List<Disciplina> getDisciplinas();

  }

  String getNomeDoAluno();

  List<Fatura> getFaturasEmAberto();

  List<Disciplina> getDisciplinas();

}

Para validar nossa resposta, teríamos assertivas como:

assertThat(res.getNomeDoAluno(), equalTo(aluno.getNome()));
assertThat(res.getFaturasEmAberto().size(), equalTo(3));
assertThat(res.getDisciplinas().size(), equalTo(5));

Logo, teremos nosso teste com a seguinte estrutura:

@Test
public class TesteDeRelatorioDeAlunoGen {

  @Inject
  private RelatorioDeAlunoGen gen;

  @Inject
  private SqlUnit sqlUnit;

  @BeforeClass
  public void prepararSqlUnit() {
    sqlUnit.loadEntitySet(AlunosFalso.class);
 
    sqlUnit.truncate(DisciplinasFalso.class);
    sqlUnit.truncate(FaturasFalso.class);
  } 

  public void deve_gerar_relatorio_de_aluno() {
    Aluno aluno = AlunosFalso.ALUNO_1;
    LocalDate vencimento = new LocalDate(2013, 8, 9);

    novaFatura(aluno, vencimento);
    novaFatura(aluno, vencimento);

    novaDisciplina(aluno);
    novaDisciplina(aluno);

    RelatorioDeAluno res = gen.gerarDe(aluno);

    assertThat(res.getNomeDoAluno(), equalTo(aluno.getNome()));
    assertThat(res.getFaturasEmAberto().size(), equalTo(2));
    assertThat(res.getDisciplinas().size(), equalTo(2));
  }

  private void novaDisciplina(Aluno aluno) {
    Disciplina disciplina = new ConstrutorDeDisciplinaFalso()
	.aluno(aluno)
	.nome("Disciplina XPTO")
	.novaInstancia();

    sqlUnit.add(disciplina).insert();
  }
  private void novaFatura(Aluno aluno, LocalDate vencimento) {
    Fatura fatura = new ConstrutorDeFaturaFalso()
	.aluno(aluno)
	.valor(500.5)
	.vencimento(vencimento)
	.novaInstancia();
    sqlUnit.add(fatura).insert();
  }

}

Observe que nesse teste não utilizamos contra e prova como nos teste de buscadores e cache. É desnecessário pois nesse teste nossa resposta é gerada de algo que não ainda não existe, já no teste de buscar por exempplo, nossa resposta é gerada a partir de algo que já existe que são os objetos falsos.

Organizando o teste corrente em blocos lógicos facilita na correção do código ou na replicação desse teste, por exemplo se quisermos fazer outro teste em que o aluno não tem nenhum fatura vencida, então poderíamos replica o mesmo assim:

public void deve_gerar_relatorio_de_aluno_sem_fatura_vencida() {
  Aluno aluno = AlunosFalso.ALUNO_2;
  LocalDate vencimento = new LocalDate()
	.plusDays(30);

  novaFatura(aluno, vencimento);

  novaDisciplina(aluno);
  novaDisciplina(aluno);

  RelatorioDeAluno res = gen.gerarDe(aluno);

  assertThat(res.getNomeDoAluno(), equalTo(aluno.getNome()));
  assertThat(res.getFaturasEmAberto().size(), equalTo(0));
  assertThat(res.getDisciplinas().size(), equalTo(2));
}

Foi possível aproveitar todo o código do primeiro testes, alterarando somente os dados de entrada. Fazemos as assertivas diretamente com os dados de entradas, com os dados vindos da resposta

Note que a os números nas assertivas para faturas e disciplinas foram obtidas através das que criamos através dos métodos novaDisciplina() e novaFatura(). Para que o nosso Gen pudesse ser testado, precisavamos antes ter faturas de um determinado aluno no banco de dados, o mesmo para disciplina. E para forçar esse cenário de teste, tivemos que criá-las manualmente sem auxílio de objetos falsos, pois era isso que espereramamos que o Gen gerrasse. Mas é preciso sempre analisar a real necessidade de criar os objetos dessa forma, caso seus testes não irão testar muitas situações distintas que exijam sempre a existência de um objeto com estado único para cada teste, então é possível usar objetos falsos já existentes, além de não alterar os objetos falsos que provalmente já estão sendo usados por outros testes, isso consequentemente evitará quebrar esses mesmos testes. Caso contrário será necessário criar objetos com o estado que o seu teste precisa testar, como no exemplo que precisavamos criar um cenário que o aluno não tivesse nenhuma fatura vencida.

Onde detalhe muito importante é fazer correntamente a implementação do método prepararSqlUnit(), análogo como já fazemos nos testes dos buscadores, para que os objetos referentes a AlunosFalso, DisciplinasFalso e FaturasFalso estejam persistidos no banco de dados ou para que as tabelas referentes a essas entidades sejam truncadas antes do teste, caso contrário o Gen não funcionará.

Códigos-fonte

Aluno.java
RelatorioDeAluno.java
AlunosFalso.java
TesteDeRelatorioDeAlunoGen.java
Fatura.java
Disciplina.java
ConstrutorDeDisciplinaFalso.java


 
 

objectos, Fábrica de Software LTDA

  • R. Demóstenes, 627. cj 123
  • 04614-013 - Campo Belo
  • São Paulo - SP - Brasil
  • +55 11 5093-8640
  • +55 11 2359-8699
  • contato@objectos.com.br