JUnit - testar métodos private

Eu continuando minha saga sobre testes unitarios, estou com 1 duvida de como eu vou testar métodos privates da minha classe.

eu tenho 1 método public que chama 3 métodos privates internamente que são muito importantes e gostaria de testar esses 3 métodos em separado.

Isso é possivel?

Obrigado

[quote=alex.lopes]Eu continuando minha saga sobre testes unitarios, estou com 1 duvida de como eu vou testar métodos privates da minha classe.

eu tenho 1 método public que chama 3 métodos privates internamente que são muito importantes e gostaria de testar esses 3 métodos em separado.

Isso é possivel?

Obrigado[/quote]

Coloca eles como protected ou coloca a classe de teste com classe interna (isso é feio… :roll: ).

Nao. Teste as interacoes desses metodos privados com outros objetos, usando mocks :wink:

http://jmock.codehaus.org :smiley:

[quote=cv]Nao. Teste as interacoes desses metodos privados com outros objetos, usando mocks :wink:

http://jmock.codehaus.org :D[/quote]

Dei uma lida na documentação, mas eu nao entendi muito bem o seu funcionamento… vc poderia dar um exemplo?

Vamos supor que eu tenho a seguinte classe abaixo

public class Teste {
  public void matematica() {
    System.out.println(soma(1,1);
  }

  private int soma(int valor1, int valor2) {
    return valor1 + valor2;
  }
}

Claro que é algo bem simples, mas gostaria de testar se o método soma está realmente funcionando OK.

Como eu poderia fazer isso?

Obrigado

http://jmock.codehaus.org/getting-started.html
?

Se estiver com preguiça, pode usar Reflecion, mas é trabalho a toa dado o link que o cv passou.

[quote=alex.lopes]Vamos supor que eu tenho a seguinte classe abaixo

public class Teste {
  public void matematica() {
    System.out.println(soma(1,1);
  }

  private int soma(int valor1, int valor2) {
    return valor1 + valor2;
  }
}

Claro que é algo bem simples, mas gostaria de testar se o método soma está realmente funcionando OK.

Como eu poderia fazer isso?[/quote]

Repare que essa classe mistura o codigo de negocios (o metodo soma()) com apresentacao (o metodo matematica()). Em qualquer coisa maiorzinha, voce vai fazer um esforco a mais pra separar os dois, e ai o problema some :wink:

De qualquer maneira, pra tornar esse codigo mais “testavel”, voce pode fazer com que o PrintStream a ser usado pelo metodo matematica() seja passado externamente:

[code]public class Teste {
public Teste() {}

public Teste(PrintStream out) {
this.out = out;
}

private PrintStream out = System.out; // default

public void matematica() {
out.println(soma(1,1);
}

private int soma(int valor1, int valor2) {
return valor1 + valor2;
}
}[/code]

No teste, basta instanciar a classe usando outro PrintStream, por exemplo:

[code]testMatematicaShouldAlwaysPrint2() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(out);

Teste t = new Teste(ps);

t.matematica();

byte[] res = out.toByteArray();
assertEquals(‘2’, res[0]);
assertEquals(’\n’, res[1]);

}[/code]

Que tal? :wink:

Legal CV
o exemplo da Matematica e Soma foi 1 exemplo besta, apenas para “tentar” mostrar como eu poderia criar 1 teste em 1 método private.
Sua solução para esse caso resolve legal, mas o que eu gostaria mesmo era testar 1 método private, para as regras de negócios do meu projeto oficial e não apenas do seguinte teste.

Eu estou tentando fazer o seguinte teste usando o JMock (estou usando o cgilib para testar classe concreta)

public class TesteClassTest extends MockObjectTestCase {

	public void testSomaDentroCalculo() {
		Mock mockSoma = mock(TesteClass.class);
		TesteClass testeClass = new TesteClass();
		mockSoma.expects(once()).method("soma").with(eq(1));
		testeClass.matematica();
	}
}

O método soma é private, e da erro quando tento rodar, falando que não encontrou o método “soma”

Alex, revise o seu design - considere, por exemplo, tornar o metodo publico (ora essa, afinal, se ele eh tao significativo que o seu teste precisa testa-lo explicitamente, pq nao?) ou introduzir umas interfaces aqui e ali. :wink:

Que tal testar o método publico que irá invocar o método private e fazer os testes possiveis de erros e acertos do private?

Digo isso pq eu mexo assim, geralmente quando eu testo uma Action do WW, tem o método execute() que é publico, ele fatalmente chama outros métodos, vamos supor que ele chame um método private que irá verificar se o usuário está na Sessão. eu monto dentro do testCase 1 opção testando se o usuário está e outra se não está e testo o retorno esperado pelo método execute().

Pelo menos pra mim está funcionando assim legal. :wink:

Certo CV, vou dar uma re-analisada na minha estrutura e qualquer coisa eu faço como o ManchesteR disse.

Outra coisa que nao me entra na cabeça como eu posso fazer teste de objetos do banco de dados usando os Mocks.

por exemplo, eu tenho dentro de um DAO o método insert (estou usando Hibernate)

public void insert(Clientes clientes) {
  Session session = sessionFactory.openSession().
  ....
  ....
  session.save(clientes);
  ....
  ....
}

Dentro por exemplo da Action “CadastroClientes” eu quero testar pra ver se realmente o objeto foi salvo no banco.

public String execute() {
  ...
  ...
  DAO.insert(clientes);
  ...
}

Eu li em vários lugares (inclusive em livros de XP) que nao deve-se usar o banco de dados para fazer teste unitarios, mas esse caso eu particularmente não acho uma solução de evitar o banco e usar Mock nisso, pois quando ele chamar o DAO.insert, vai chamar o método do meu DAO que faz uma inserção no banco através do Hibernate.

Faca um mock do DAO, ueh!

Lá vai a minha solução sem utilizar Mock, pode ser feia, suja, mas parece que está funcionando legal. Eu particularmente não consegui “descobrir” como os mocks podem substituir todos os testes em relação a banco de dados, por isso estou utilizando um banco de dados apenas para teste, deixa eu explicar melhor.

Tenho o banco de dados do sistema, onde todas as pessoas o utilizam e fica armazenado no servidor de desenvolvimento. Eu crio outro banco de dados identico a esse, mas localmente na minha maquina. Uso o hibernate e dentro dele uso 2 arquivos de configurações diferentes, hibernate.conf.xml e hibernate-test.conf.xml. O primeiro é ligado ao servidor de desenvolvimento e o segundo é local, o detalhe do segundo que ele tem a opção de toda vez que rodar “dropar” e “criar” as tabelas, assim deixando os dados sempre vazios.

Dentro do sistema, tenho um servlet que inicializa o SessionFactory dentro de 1 DAO, esse servlet é executado quando meu AS sobe, no caso eu passo como parametro qual o arquivo de configuração eu vou usar, por exemplo

DAO dao = new DAO();
dao.start("/hibernate.conf.xml");

Dentro dos meus testCase, eu inicio o DAO mas passando como parametro o /hiberante-test.conf.xml.

Vamos supor que eu tenho o seguinte caso. Tenho uma Action chamada “Welcome” que será executada quando o sistema rodar. Essa Action verifica se existe algum usuário cadastrado no banco de dados com Level = 9, se nao houver nenhum usuario assim, deve redirecionar para uma pagina chamada wizard.jsp, senao chamada a welcome.jsp.

Dentro dela, eu preciso fazer um “select” no banco para testar se existe 1 usuário com level = 9. Assim meu testCase para essa Action ficaria mais ou menos assim.

DAO dao;
public void setUP() {
  dao = new DAO();
  dao.start("/hibernate-test.conf.xml"); //Iniciar o Hibernate com BD de teste, lembrando que a base estará vazia, assim nenhum usuario estará cadastrado.
}

public void testWelcomeSemAdmin() {
  //Testar quando nao tem Admin e verificar se ele está chamando um return "wizard"
  WelcomeAction welcome = new WelcomeAction();
  assertEquals("wizard",welcome.execute());
}

public void testWelcomeComAdmin() {
  insertAdmin(); //aqui irei adicionar 1 admin manualmente (método embaixo)
  assertEquals(Action.SUCCESS,welcome.execute()); //Quando testar aqui, teoricamente terá o usuário cadastrado..
}

private void insertAdmin() {
  Users users = new Users();
  users.setName("teste");
  users.setLevel(9);
  DAO.insert(users); //inseri no meu Banco de Teste
}

Basicamente é assim que eu faço teste referente a banco de dados. Particularmente eu NUNCA vi isso em nenhum lugar, mas até agora parece que está legal.

Alex, se quiser, eu estou terminando a implementar essa parte em um projetinho open-source, quando eu tiver algo “palpável” eu posso te mostrar, só me da 1 toque.

Pra galera que nao concorda com isso, poderia ajudar falando uma solução mais elegante e correta para trabalhar com Teste Unitário em Banco de Dados.

Obs: eu respondi o tópico na faculdade, sem java, sem nada, entao pode haver erros de digitação no código.

Espero ter ajudado

interessante a idéia, eu cheguei a pensar em fazer o teste usando realmente a base de dados, mas NÃO tinha pensando em usar uma base de dados local apenas para teste, isso vai melhorar muito a performance.

O que me deixa emcabulado, é que nos livros que eu li, eles sempre mandam fazer mock para testar banco de dados, e eu não intendi como eu poderia por exemplo, usar mock para testar a validação de 1 usuário.