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