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

Implementando Buscador: Testes

Introdução

Neste procedimento falaremos sobre o teste de buscar. O teste de buscar possibilita verificar se as informações que estamos inserindo em nosso sistema estão persistindo no banco de dados. Além disso, podemos nos certificar através de Dados de testes razoáveis se as implementações e filtros dos testes de busca estão funcionando corretamente.

Na maioria das vezes é impossível testar nosso buscador para todas as condições de busca, pois teríamos uma quantidade muito grande de testes a fazer ou seja teríamos que realizar um Teste absoluto com cada uma dessas condições.

Uma alternativa para testar nosso buscador de um jeito mais eficiente é utilizando Dados de testes razoáveis, ou seja neste procedimento vamos testar apenas o que consideramos pontos críticos de nossa implementação.

Antes de iniciar

Este artigo assume conhecimento prévio em:

  • EspecificaçãoTODO
  • TDD

Passo-a-passo

Especificação

Siga o checklist abaixo:

Quais os atributos da entidade devemos testar? help!
Quais métodos de busca devemos testar? help!

Quais os atributos da entidade devemos testar?

Devem ser testados todas as propriedades cujo valor está direta ou indiretamente associado a informações vindas do banco de dados. Em 80% dos casos é seguro dizer que se deve testar todos os getters definidos na interface. No nosso exemplo, portanto:

  public interface Funcionario {

    int getId();

    String getMatricula();

    String getNome();

    LocalDate getDataNascimento();

    DateTime getDataAdmissao();
    
    DateTime getDataDemissao();
    
    Departamento getDepartamento();

  }

Quais métodos de busca devemos testar?

Idealmente você deve ser capaz de decidir quais métodos serão necessários a partir do contexto em que seu buscador será utilizado. Exemplos:

  • Há uma parte do sistema que irá exibir o funcionário baseado em sua matrícula.

  • Há uma página que lista os aniversariantes do mês corrente.

  • Funcionários com mais de 3 anos de casa precisam receber um e-mail com seus novos benefícios.

Para este artigo, e por motivos puramente didáticos, ficou decidido:

    1. Buscar funcionários pelo id
  • 1.2 Buscar funcionários pela matrícula

Implementação

Siga o checklist abaixo:

Criando classe com registros falsos
Estruturar o código com as Anotações, Buscadores, sqlUnit, métodos e funções necessários
O que o Teste de Buscar é capaz de verificar?

Criando classe com registros falsos

Antes de iniciar nosso Teste De Buscar, iremos popular nosso banco de dados com algumas informações “não reais” que servirão apenas para que possamos visualizar se nosso teste e sua respectiva implementação estão funcionando corretamente.

Primeiramente vamos criar uma classe de Funcionários com informações falsas. Para isso vamos utilizar atalhos do eclipse, selecionamos o pacote desejado e usamos as teclas Ctrl+N: Esta classe será implementada por EntitySet e iremos sobrescrever dois métodos: truncate que trunca nossa tabela zerando os registros e em seguida o load que servirá basicamente para inserir nosso registro no banco de dados através do método batchInsert.

Sendo assim nossa classe ficará parecida com o exemplo abaixo:

public class FuncionariosFalso implements EntitySet {

   	public static final Funcionario FUNC_01_JOSE= nova()
  	 .id(1)
  	 .matricula(29501)
     .nome("jose")		         
  	 .dataNascimento(localDate(1990, 1 ,1))
  	 .dataAdmissao(dateTime(2010))
  	 .novaInstancia();
FuncionariosFalso() { } @Override public void truncate(Truncate truncate) {
  	  truncate.table("EMPRESA_TESTE.FUNCIONARIO");
  		}
@Override public void load(SqlUnit sqlUnit) {
 	  sqlUnit.batchInsert(FUNC_01_JOSE);
  		}
private static ConstrutorDeFuncionarioFalso nova() {
  	  return new ConstrutorDeFuncionarioFalso();
  		}  

Estruturar o código com as Anotações, Buscadores, sqlUnit, métodos e funções necessários

Passo-a-passo

Primeiramente, criamos a classe de Teste no pacote desejado. Para isso basta selecionar o pacote e utilizar o atalho Ctrl+N:

public class TesteDeBuscarFuncionario {
}

Agora inserimos a notação @Test, para que o plugin do eclipse TestNG consiga executar esta classe como teste:

@Test
public class TesteDeBuscarFuncionario {
}

Inserimos uma nova notação @Guice com o módulo correto de nosso projeto:

@Test
@Guice(modules = { ModuloDeTesteObjectosDojo.class })
public class TesteDeBuscarFuncionario {
}
Geralmente, para cada projeto serão utilizados diferentes Módulos. Portanto atente-se a isso quando for implementar seus Testes!

Vamos agora declarar uma instância da interface BuscarFuncionario que será testada. Note que esta interface ainda não existe e por isso o eclipse irá deixar este texto destacado para indicar um erro:

  @Inject
  private BuscarFuncionario buscarFuncionario;
  
}

Utilizamos as teclas Ctrl + ponto para verificar o código com erro e em seguida usamos o atalho Ctrl+1 para o eclipse indicar possíveis soluções de reparo, vamos escolher a opção criar Interface.

Após criar a interface voltamos à classe TesteDeBuscarFuncionario e inserimos a notação @Inject logo acima de nossa instância buscarFuncionario, esta notação do Guice servirá para inicializar a instância.

Por fim, declaramos uma instância de SqlUnit para podermos trazer os conteúdos das tabelas do banco de dados que iremos utilizar em nosso teste. Temos que inserir sempre a tabela que estamos utilizando, neste caso FuncionariosFalsos e todas as tabelas que estiverem se relacionando com ela.

Em nosso exemplo, utilizamos classes com registros falsos que foram inseridos no banco de dados.

Para implementar automaticamente o método prepararSqlUnit() basta digitarmos o seguinte comando _SqlUnit, em seguida usando o atalho Tab + Space vamos ter acesso a algumas opções, vamos escolher a opção para o método prepararSqlUnit().

Se a classe FuncionariosFalsos não estiver implementando EntitySet, não será possível usar o atalho acima.

Dentro do método prepararSqlUnit() vamos chamar todas as classes de registros falsos que estamos utilizando em nosso teste.

@Test    
@Guice(modules = { ModuloDeTesteObjectosDojo.class })
public class TesteDeBuscarFuncionario {

  @Inject
  private BuscarFuncionario buscarFuncionario;
  
  @Inject
  private SqlUnit sqlUnit;

  @BeforeClass
  public void prepararSqlUnit() {
    sqlUnit.loadEntitySet(FuncionariosFalsos.class);
  }

}

Se esquecermos de colocar alguma classe que está sendo utilizada em nosso teste, poderão ocorrer alguns erros conforme o exemplo abaixo:

FAILED CONFIGURATION: @BeforeClass prepararSqlUnit

Podemos criar diversos métodos para testar nosso buscador. Em nosso exemplo, vamos criar um método para buscar Funcionário de acordo com o número da matrícula.

Nosso método terá acesso público e não terá retorno, ficando com a seguinte assinatura:

public void busca_por_matricula() {

}

Agora vamos selecionar um funcionário da classe FuncionariosFalsos de nosso banco de dados e em seguida extrair dele o atributo matrícula:

public void busca_por_matricula() {

Funcionario funcionario = FuncionariosFalsos.FUNC_01_JOSE;	

String matricula =  funcionario.getMatricula();
}	    

Devemos montar uma “prova” com este funcionário que sabemos que está inserido em nosso banco de dados para comparar com o funcionário trazido por nosso buscador. Desta forma, estamos verificando se o método de buscar está de fato trazendo as informações esperadas.

Para fazer esta comparação, primeiramente vamos criar uma classe chamada FuncionarioToString que servirá para transformar cada atributo da prova e da resposta de tipo Funcionario para tipo String.

Inicialmente FuncionarioToString será uma Inner Class ou seja uma classe interna, que posicionamos logo abaixo do código de nossa classe TesteDeBuscarFuncionario:

private static class FuncionarioToString implements Function<Funcionario, String>{

}

Agora o eclipse selecionará esta classe para indicar que devemos implementar o método apply de Funcion, vamos utilizar o atalho Ctrl+ponto e em seguida Ctrl+1 para escolher a opção implementar método.

Em seguida usando os mesmos atalhos vamos transformar o método apply em público.

É muito importante que todos os atributos da interface Funcionario estejam presentes no método apply da classe FuncionarioToString, pois ao final do teste faremos asserts que comparam nossa prova com a resposta trazida do banco de dados e este método estará envolvido nesta comparação. Caso algum atributo não esteja sendo comparado é possível que nosso teste não aponte erros de implementação como por exemplo erros na classe Loader.

Para maiores detalhes veja: O que o Teste de Buscar é capaz de verificar?

Agora que nossa Inner class está pronta vamos transformá-la em uma classe externa. Para isso vamos adicionar comentários do autor através do atalho alt+shift+j e em seguida vamos deixar a classe externa com o comando alt+t+v, desta forma o acesso da classe será default.

Nossa classe FuncionarioToString ficará parecida com o exemplo abaixo:

class FuncionarioToString implements Function<Funcionario, String> {
  		@Override
  		public String apply(Funcionario input) {
	  return Objects.toStringHelper(input)
    	.addValue(input.getId())
    	.addValue(input.getMatricula())
    	.addValue(input.getNome())
    	.addValue(input.getDataNascimento())
    	.addValue(input.getDataAdmissao())
    	.addValue(input.getDepartamento().getId())
    	.toString();
  		  }
	}

Note que no exemplo acima, temos um relacionamento da tabela Funcionario com Departamento. Ou seja, dentro de nossa entidade Funcionario existe uma referência para Departamento, por isso extraímos o campo Id de departamento.

    .addValue(input.getDepartamento().getId())   

Voltamos agora para a classe TesteDeBuscarFuncionario, e iremos criar um método em nosso buscador que pesquise funcionários a partir de uma matrícula.

Para isso, usaremos a instância buscarFuncionario, em seguida digitamos ponto, o nome do método que queremos criar e o parâmetro que utilizaremos na busca.

buscarFuncionario.buscarPorMatricula(matricula);

O método que escrevemos ainda não existe e por isso este campo apresentará erro. Agora utilizando os atalhos Ctrl+ponto para selecionar o código com erro e em seguida Ctrl+1 para corrigir então vamos escolher a opção criar método.

Em seguida vamos transformar nossa prova e nossa resposta em String

    public void busca_por_matricula() {
	Funcionario funcionario = FuncionariosFalsos.FUNC_01_JOSE;	
    String matricula =  funcionario.getMatricula();

    String prova = FuncionarioToString().aplly(funcionario);

	Funcionario pojo = buscarFuncionario.buscarPorMatricula(matricula);
	String res =  FuncionarioToString().aplly(pojo);
}	   

Utilizando o método assertThat vamos verificar se a resposta trazida por nosso buscador está correta:

	assertThat(res, equalTo(prova));

Nossa classe TesteDeBuscarFuncionario seguindo o exemplo ficou da seguinte forma:

@Test    
@Guice(modules = { ModuloDeTesteObjectosDojo.class })
public class TesteDeBuscarFuncionario {

  @Inject
  private BuscarFuncionario buscarFuncionario;
  
  @Inject
  private SqlUnit sqlUnit;

  @BeforeClass
  public void prepararSqlUnit() {
    sqlUnit.FuncionariosFalsos;
  }
  
    public void busca_por_matricula() {
    Funcionario funcionario = FuncionariosFalsos.FUNC_01_JOSE;	
    String prova = FuncionarioToString().aplly(funcionario);

    String matricula =  funcionario.getMatricula();
    
	Funcionario pojo = buscarPorMatricula(matricula);
	String res =  FuncionarioToString().aplly(pojo);
	    
	assertThat(res, equalTo(prova)); 
	}
   
}

O que o Teste de Buscar é capaz de verificar?

O Teste de Buscar é capaz de verificar a funcionalidade do Loader provando que ele de fato consegue capturar os valores de uma entidade no banco de dados. Além disso, problemas com a implementação da classe Loader também podem ser percebidos através do teste.

Por exemplo, digamos que ao sobrescrever os métodos da classe Construtor dentro da classe Loader utilizamos os atalhos para copiar e colar nos métodos getDataNascimento e getDataAdmissao e não percebemos ao fazer isso que acabamos retornando a mesma coluna do banco de dados para as duas datas.

private class Loader implements Funcionario.Construtor { @Override
public LocalDate getDataNascimento() {
  return rs.getLocalDate("DATA_NASCIMENTO");
}

@Override
public LocalDate getDataAdmissao() {
  return rs.getLocalDate("DATA_NASCIMENTO");
}

Este erro será exposto através de nosso teste, pois quando fizermos a comparação de nossa resposta com nossa prova verificaremos que elas são diferentes (possuem data de admissão diferentes) e nosso teste falhará.

	assertThat(res, equalTo(prova)); 

Isso ocorre porque somente a resposta utilizou a classe Loader através da implementação de seu buscador. Desta forma, em nosso exemplo na prova consta a data de admissão correta e na resposta na data de admissão constará a data de nascimento.

Neste exemplo fica explícita a importância da classe FuncionarioToString. Se o atributo dataAdmissao não fosse colocado no método apply esse erro não seria percebido, pois ao usar o método assertThat para comparar a resposta com a prova não haveria comparação entre o campo dataAdmissao delas e por isso o teste não apresentaria falha.

Através do teste também podemos verificar se estamos de fato utilizando dados de teste razoáveis. Por exemplo se desejarmos fazer uma busca por todos os funcionários com o nome Maria e em nossa classe de FuncionariosFalso tivermos cadastrado quatro funcionários falsos com este mesmo nome, nosso teste pode não verificar se nosso método de busca está funcionando corretamente. O ideal seria criarmos falsos com nomes diferentes, assim quando buscássemos por nome somente um funcionário seria trazido pela consulta.

A classe de teste de buscar verifica ainda se o método que realiza a inserção dos dados no banco de dados está funcionando corretamente, ou seja, verifica se os registros falsos que criamos estão de fato sendo inseridos no banco de dados. Quando nossa resposta é um List, a primeira coisa que verificamos é o tamanho desta lista. Desta forma, garantimos que nossa consulta de fato trouxe algum resultado e que nossos registros falsos foram inseridos com sucesso no banco de dados.

Exemplo

assertThat(res.size(), equalTo(1);

Note que se comparássemos o tamanho do List da resposta com o da prova, nosso teste não verificaria quando a inserção no banco de dados não estivesse ocorrendo.

	  		assertThat(res.size(), equalTo(prova.size());

Digamos que ocorreu um problema na inserção e não há registro no banco de dados. Neste caso,nosso teste passaria pois a quantidade de registros da resposta seria igual ao da prova, ou seja. nenhum.

Casos especiais

Criando Filtros
Filtros utilizando outras Entidades
Entidades com relacionamentos
Métodos que retornam listas
Métodos que retornam iteradores
Testes incompletos

Criando Filtros:

Os filtros permitem que o usuário realize uma pesquisa mais refinada em nosso sistema através de alguma informação específica. Por exemplo, se o usuário quiser realizar uma busca por funcionários dada uma determinada matrícula é necessário a existência de um filtro para realizar esta busca.

Antes de criarmos os filtros, vamos criar um método chamado pagePorTodos() que listará todos os funcionários cadastrados no banco de dados. E a partir deste método faremos os filtros.

Vamos criar um método na classe que irá testar se o método pagePorTodos() do buscador está funcionando, vamos considerar que possuímos três funcionários cadastrados no banco de dados:

Inicialmente trazemos todos os funcionários cadastrados através do método getTodos()

public void page_por_todos() {
List<Funcionario> contra = FuncionariosFalso.getTodos();
List<String> prova = transform(contra, new FuncionarioToString());

Em seguida criamos um objeto da classe FakeRequestWrapper e ele será passado como parâmetro para nosso método de busca.

Seguindo o padrão de desenvolvimento TDD, primeiramente vamos criar um método em nossa classe TesteDeBuscar que servirá para testar nossa implementação. Para isso, vamos escrever o nome do método e em seguida criá-lo utilizando o atalho Ctrl+1 e escolher a opção criar classe.

Desta forma o método pagePorTodos foi criado dentro da classe BuscarFuncionario

FakeRequestWrapper wrapper = new FakeRequestWrapper();
PageList<Funcionario> page = buscarFuncionario.pagePorTodos(wrapper);
List<Funcionario> list = page.getRows();
List<String> res = transform(list, new FuncionarioToString());

Por fim, verificamos se a quantidade de registros trazidos está correta, ou seja se o List chamado res contém agora os três funcionários cadastrados.

assertThat(res.size(), equalTo(3));
assertThat(res, equalTo(prova));   }

O método completo ficou conforme abaixo:

public void page_por_todos() {
List<Funcionario> contra = FuncionariosFalso.getTodos();
List<String> prova = transform(contra, new FuncionarioToString());

FakeRequestWrapper wrapper = new FakeRequestWrapper();

PageList<Funcionario> page = buscarFuncionario.pagePorTodos(wrapper);
List<Funcionario> list = page.getRows();
List<String> res = transform(list, new FuncionarioToString());

assertThat(res.size(), equalTo(3));
assertThat(res, equalTo(prova));
  	}

Este método será utilizado em nossas classes que habilitam a listagem web de nosso sistema.

A partir do método que criamos no exemplo acima podemos criar os filtros desejados. Vamos criar um filtro que liste todos os funcionários com uma determinada data de admissão: Quando testamos um filtro na classe TesteDeBuscarFuncionario, simulamos a situação em que o usuário estaria digitando um parâmetro para a busca, neste caso a data de admissão.

O método para testar nosso filtro é muito parecido com o método page_por_todos() que acabamos de criar.

Vamos usar um funcionário falso como prova e neste caso vamos extrair sua data de admissão:

public void filtro_por_data_admissao() {
Funcionario funcionario = FuncionariosFalso.FUNC_01_JOSE;
String prova = new FuncionarioToString().apply(funcionario);
LocalDate data = funcionario.getDataAdmissao();

Agora usaremos esta data em nosso filtro, ou seja, nosso método pagePorTodos que antes trazia todos os funcionários cadastrados agora deverá trazer apenas os funcionários que possuam data de admissão igual a informada.

FakeRequestWrapper wrapper = new FakeRequestWrapper();
wrapper.put("data", data);

PageList<Funcionario> pager = buscarFuncionario.pagePorTodos(wrapper);
List<Funcionario> list = pager.getRows();
List<String> res = transform(list, new FuncionarioToString());

Note que estamos passando dois parâmetros para o método put acessado por nosso objeto wrapper:

  • O primeiro é uma String que funciona como uma “chave” para um Map que será criado, além disso esse nome será exibido para o usuário quando o filtro for utilizado.

  • O segundo trata-se do valor atribuído à chave, em nosso caso é a data de admissão extraída.

O método put retornará um Map que contém todos os registros de funcionários associados a data de admissão que passamos como parâmetro.

Consideraremos que dos três funcionários cadastrados em nosso sistema, somente um foi admitido na data informada.

assertThat(res.size(), equalTo(1));
assertThat(res.get(0), equalTo(prova));   }  

Filtros utilizando outras Entidades:

Em alguns casos desejamos criar um filtro para refinar uma busca não somente por um atributo de minha entidade mas também por uma outra entidade que está direta ou indiretamente relacionada com ela.

Vamos utilizar como exemplo a entidade Empresa, esta entidade possui vários departamentos e estes por sua vez possuem funcionários.

Podemos observar que neste caso não há relação direta entre as entidades Empresa e Funcionario, porém desejamos fazer uma listagem de funcionários de uma determinada empresa.

Esta listagem pode ser realizada, pois estas entidades estão indiretamente ligadas através da entidade Departamento.

Antes de criarmos os filtros, iremos criar um método em nossa classe de teste para realizar esta busca. Sempre que montarmos uma prova procuramos utilizar o caso mais específico possível. Em nosso exemplo, nossa prova será a empresa BBB pois vamos considerar que esta seja a empresa que possui a menor quantidade de funcionários, no caso 2 funcionários. Digamos que exista uma empresa chamada AAA e que ela possua 10 funcionários, ela não seria uma boa prova pois seria difícil verificar se todos os funcionários da empresa que estão cadastrados em nosso banco de dados estão de fato sendo trazidos por nossa pesquisa.

  	public void page_por_empresa_key() {
	Empresa empresa = EmpresasFalso.BBB;
	EmpresaKey empresaKey = new EmpresaKey(empresa);

	FakeRequestWrapper wrapper = new FakeRequestWrapper();

No exemplo que estamos utilizando seria trabalhoso criar uma prova manualmente. Por isso vamos criar dentro da classe FuncionariosFalso um método que dada uma empresa retorne todos os funcionários a ela relacionados.

Vamos criar um método chamado porEmpresaKey dentro de FuncionariosFalso. Para isso, primeiramente escrevemos o nome do método em nosso teste, como ele ainda não existe o eclipse apontará erro então, utilizamos o atalho Ctrl+ponto para selecionar o código com erro e em seguida utilizamos as teclas Ctrl+1 para corrigir e escolhemos a opção criar método.

List<Funcionario> contra = FuncionariosFalso.porEmpresaKey(empresaKey);
List<String> prova = transform(contra, new FuncionarioToString());

_Vamos implementar este método na classe _FuncionariosFalsos __:

Quando utilizamos os atalhos do eclipse a assinatura do método foi criada automaticamente, agora vamos definir um retorno para este método.

Sabemos que nosso retorno será uma lista contendo todos os funcionários de uma determinada empresa, mas para retornamos esta lista, inicialmente iremos precisar de uma estrutura chamada ListMultimap. Como o próprio nome diz esta estrutura funciona como um map que “internamente” utiliza um List permitindo armazenar vários valores a partir de uma única chave, ou seja, em nossa consulta por funcionários usando como chave a empresa BBB, deveremos encontrar dois funcionários, conforme mencionado no texto acima.

Nosso método ficará da seguinte forma:

public static List porEmpresaKey(EmpresaKey empresaKey) {
return empresaKeyMap.get(EmpresaKey);
  	}

O eclipse apontará um erro pois empresaKeyMap ainda não existe, sendo assim vamos criá-lo. empresaKeyMap é uma constante do tipo ListMultimap que receberá como chave a EmpresaKey e como valor a interface Funcionario.

private static final ListMultimap<EmpresaKey, Funcionario> empresaKeyMap = ArrayListMultimap
  .create();

Quando os dados forem inseridos no sistema, um método que chamaremos de map será chamado. Para criar este método primeiro digitamos seu nome e em seguida usamos os atalhos Ctrl+ponto e Ctrl+1 e escolhemos a opção criar método.

@Override
  	public void load(SqlUnit sqlUnit) {
sqlUnit.batchInsert(todos);
maps();
  	}

O map funcionará da seguinte forma: Inicialmente ele fará uma “limpeza” nos registros de empresaKeyMap utilizando o método clear:

private static void maps() {
empresaKeyMap.clear();

Em seguida ele irá inserir os dados de cada funcionário e sua respectiva chave em empresaKeyMap. Para isso faremos um for each que percorrerá todos os funcionários cadastrados e associará cada um deles a empresa que trabalha e armazenará dentro de empresaKeyMap através do método put.

private static void maps() {
empresaKeyMap.clear();

for (Funcionario funcionario : todos) {
  Departamento departamento = funcionario.getDepartamento();      
  Empresa empresa = departamento.getEmpresa();
  
  EmpresaKey empresaKey = new EmpresaKey(empresa);

  empresaKeyMap.put(empresaKey, funcionario);
  }
  	}

_Agora vamos retornar à classe _TesteDeBuscarFuncionario __:

Para criar o método pagePorEmpresaKey vamos utilizar o mesmo atalho citado anteriormente: Ctrl+ponto e Ctrl+1 e escolhemos a opção criar método.

PageList<Funcionario> list = buscarFuncionario.pagePorEmpresaKey(empresaKey, wrapper);
List<Funcionario> rows = list.getRows();
List<String> res = transform(rows, new FuncionarioToString());

O método getRows() retorna um List com todas as linhas de nossa PageList. Agora faremos a verificação para saber se nossos métodos estão de fato trazendo o resultado esperado.

assertThat(res.size(), equalTo(2));
assertThat(res, equalTo(prova));

Nosso método pronto ficará conforme abaixo:

public void page_por_empresa_key() {
Empresa empresa = EmpresasFalso.BBB;
EmpresaKey empresaKey = new EmpresaKey(empresa);

FakeRequestWrapper wrapper = new FakeRequestWrapper();

List<Funcionario> contra = FuncionariosFalso.porEmpresaKey(empresaKey);
List<String> prova = transform(contra, new FuncionarioToString());

PageList<Funcionario> list = buscarFuncionario.pagePorEmpresaKey(empresaKey, wrapper);
List<Funcionario> rows = list.getRows();
List<String> res = transform(rows, new FuncionarioToString());

assertThat(res.size(), equalTo(2));
assertThat(res, equalTo(prova));    }  

O método page_por_empresa_key está trazendo todos os funcionários de uma determinada empresa mas podemos refinar ainda mais nossa busca através de filtros, por exemplo utilizando o método anterior podemos pesquisar apenas por funcionários da empresa que tenham uma determinada data de admissão.

public void page_por_empresa_key_por_data_de_admissao() {
Empresa empresa = EmpresasFalso.BBB;
EmpresaKey empresaKey = new EmpresaKey(empresa);

Escolhemos um funcionário que seja da empresa BBB:

Funcionario contra = FuncionariosFalso.FUNC_02_MARIA;
String prova = transform(new new FuncionarioToString(), apply(contra); 

FakeRequestWrapper wrapper = new FakeRequestWrapper();

Agora extraímos a data de admissão deste funcionário e passamos esta informação para nosso filtro.

wrapper.put("data", contra.getDataAdmissao());

PageList<Funcionario> list = buscarFuncionario.pagePorEmpresaKey(empresaKey, wrapper);
List<Funcionario> rows = list.getRows();
List<String> res = transform(rows, new FuncionarioToString());

Consideraremos que existe apenas um funcionário da empresa com a data de admissão informada:

assertThat(res.size(), equalTo(1));
assertThat(res.get(0), equalTo(prova));   }

Entidades com relacionamentos

Ao implementar testes de buscadores nos deparamos com situações onde encontramos entidades que se relacionam no banco de dados, por exemplo: Produto e Fabricante, onde produto tem uma referência de fabricante em sua tabela, ou seja as entidades estão relacionadas entre si. Por conta disso existirão buscadores que realizarão a busca de uma entidade por outra (lembrando a questão do relacionamento entre tabelas), um exemplo disso seria buscar Produto dado seu Fabricante. Para este tipo de situação podemos utilizar um dos três casos citados abaixo.

@Inject
private BuscarProduto buscarProduto;

@Inject
private BuscarFabricante buscarFabricante;

public void busca_produto_por_fabricante() {
    Fabricante fabricante = buscarFabricante.porId(10);
    Produto res = buscarProduto.porFabricante(fabricante);
    
    assertThat(res.getId(), equalTo(20));
}

Solução B: Verificar a busca pelo relacionamento por uma entidade extraída:

  public void busca_por_superior() {
    Funcionario funcionario = buscarFuncionario.porId(5);
    Departamento departamento = funcionario.getDepartamento();
    
    Funcionario res = buscarFuncionario.porDepartame-nto(departamento);
    
    assertThat (res.getDepartamento().getId(), equalTo(4));
  }

Métodos que retornam listas

Existirão casos onde o retorno do buscador será um conjunto de entidades dentro de uma estrutura de dados, como uma lista por exemplo. Para a realização dos asserts deste tipo de estrutura devemos implementar classes internas conhecidas como funções, que são responsáveis em “quebrar” a lista que o buscador retornou em listas mais específicas, por exemplo. Assim não é preciso implementar testes que acessem propriedades como:

assertThat(res.get(0).getProduto().getId(), equalTo(1))

Vamos a implementação do teste para maior esclarecimento

@Inject
private BuscarProduto buscarProduto;

@Inject
private BuscarFabricante buscarFabricante;

public void busca_por_superior() {
  Fabricante fabricante = buscarFabricante.porId(2);
  List<Produto> res = buscarProduto.porFabricante(fabricante);

  List<Integer> ids = transform(res, new ToId());
  assertThat(ids.get(0), equalTo(1));
  assertThat(ids.get(1), equalTo(2));
}

private class ToId implements Function<Produto, Integer> {
  @Override
  public int apply(Produto input) {
    return input.getId();
  }
}

Ao implementar a função percebemos mais facilidade para realizar os asserts das propriedades, pois temos listas contendo informações específicas das entidades e não as entidades em si, acima apenas o assert da propriedade id foi realizado, por isso cuidado! Foi mantido apenas o assert desta propriedade por conta do mesmo motivo explicado acima, caso existam testes que utilizem valores de registros distintos e que verifiquem todas as propriedades da entidade, no próximo teste a ser implementado não será necessário realizar os mesmos asserts, apenas asserts que verifiquem propriedades únicas das tabelas, como chaves primárias por exemplo, caso estes testes não existam então será necessário implementar funções para cada uma das propriedades da entidade.

Métodos que retornam iteradores

Como foi mencionado acima, alguns buscadores poderão retornar um conjunto de entidades em algum tipo de estrutura de dados. Neste tópico serão abordados métodos que retornam iteradores e como realizar os devidos asserts para este tipo de métodos.

Não há muito segredo neste tipo de método, basta converter o iterador em uma lista utilizando ImmutableList.copyOf(iterador), com isso o iterador é convertido em uma lista, vamos ao teste para maiores esclarecimentos.

public void busca_iterador_por_superior() {
  Fabricante fabricante = buscarFabricante.porId(16);

  Iterator<Produto> iterator = buscarProduto.iterarPorFabricante(fabricante);

  List<Produto> res = ImmutableList.copyOf(iterator);
  assertThat(res.size(), equalTo(2));

  List<Integer> ids = transform(res, new ToId());
  assertThat(ids.get(0), equalTo(11));
  assertThat(ids.get(1), equalTo(16));
}

  private class ToId implements Function<Produto, Integer> {
  @Override
  public int apply(Produto input) {
    return input.getId();
  }
}

Novamente deve-se tomar muito cuidado com os asserts, o teste acima só é valido quando existem outros testes que verificam todas as propriedades de registros distintos, caso contrário será necessária a implementação das funções para cada propriedade da entidade.

Erros comuns

O relacionamento com outras classes, ou o teste dele, não está correto.

Os valores no banco de dados que não coincidem com aqueles nos asserts:

  public void busca_por_id() {
    Funcionario res = buscarFuncionario.porId(1);

    assertThat(res.getId(), equalTo(1));
    assertThat(res.getMaticula(), equalTo("T0033000"));
    assertThat(res.getNome(), equalTo("Brian Adamms"));
    assertThat(res.getDataNascimento(), equalTo(1980,6,01));
    assertThat(res.getDataAdmissao(), equalTo(new DateTime(2001,12,10,9)));
    assertThat(res.getDataDemissao(), equalTo(new DateTime(2012,1,3,12,30)));
  }

Erros de conversão (long, double, int, Enum, DateTime, LocalDate, etc):

rs.getId("FUNCIONARIO.DATA_NASCIMENTO")

Erros no “suffix” usado pelo buscador:

this.suffix = "SUPRIOR"

Erro de integridade dos dados no banco:

"FK_FUNCIONARIO_ID = 5" não possui correspondência em FUNCIONARIO.ID

Erro de acesso aos dados do banco:

rs.getString("FUNCINARIO_NOME");

Erro de @Inject:

private Funcionario funcionario;

Testes incompletos

São Testes em que propriedades ou colaborações (relacionamentos) não são testados. Exemplo:

public void busca_por_id() {
    Funcionario res = buscarFuncionario.porId(2)

    assertThat(res.getMatricula(), equalTo("F0050001"));
    assertThat(res.getNome(), equalTo("Priscilla Cardoso"));
}

Seguir em frente? Buscadores!

Leia mais uma vez! Revisar!

Ler códigos!

Exemplo: TesteDeBuscarFuncionario.java
Mini arquivo: mini-empresa.xml

 
 

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