eu tenho vários testes unitários aqui implementados os quais quando executados individualmente, funcionam sem problemas.
O problema é quando eu vou criar uma suite contendo todos os testes unitários. De alguma forma, o resultado de um teste está interferindo no resultado do outro teste unitário.
Exemplo:
Tenho uma classe que testa os métodos relacionado com a entidade Usuario e outra classe de teste relacionada com a classe Perfil.
Na classe de teste unitario do Perfil, tem um metodo testExcluirPerfil() que irá excluir um perfil especifico e todos os usuarios associados.
Na classe de teste unitario do Usuario há um método testBuscarTodosUsuarios() que irá retornar todos os usuários cadastrados na base.
O problema é que se há, por exemplo, 100 usuarios cadastrados no BD, e eu executo primeiro o teste unitário do perfil (cujo resultado, por exemplo seria a exclusao de 10 usuarios associados ao perfil excluido), quando eu vou executar o teste unitario da classe Usuario ocorre uma falha, pq a quantidade esperada era 100 e a obtida foi 90.
Muito estranho isso. No setUp() de cada teste unitario eu abro uma transacao e no tearDown() eu faco um rollback e fecho a transacao.
:arrow: porque o método testBuscarTodosUsuarios() espera 100 usuários? acho que no teste você não tem que esperar um número de usuários mas sim talvez apenas testar se a lista de usuários trazida não é nula
:arrow: porque você cria transação nos testes? acho que esse controle tem que ficar em outro lugar, de preferência nos métodos que rodam em um contexto transacional, ou então em outro lugar mais apropriado
além disso, qual o erro está acontecendo??
eu acho que se rodando os testes individualmente está dando certo e no suite não então pode ser que talvez a ordem de execução dos testes seja o problema, outra coisa que eu acho que pode ser problema é na hora de fechar a abrir transações, como eu falei, acho que isso não deve ser responsabilidade dos testes…
bom eu estou engatinhando ainda em escrever bons testes unitarios. Nem sei se realmente eh necessario esse nivel de detalhe que estou implementando nos testes unitarios. Mas como eu tenho uma base de testes isolada, e todos as manipulacoes que os demais testes unitarios venham a fazer nos dados da base de testes sao desfeitos (rollback), entao estou usando esse nivel de detalhe para aumentar a precisao dos testes.
Esse lance da transacao esta sendo usado por 2 motivos:
a classe que esta sendo testada, automaticamente cria uma transacao
para poder ser possivel dar um rollback nas eventuais alteracoes nos dados que os testes unitarios venham a fazer.
Entretanto, acho que ja descobri o problema. Como o pessoal aqui implementou o gerenciamento da Session do Hibernate na mão, to desconfiado que a mesma Session está sendo reutilizada entre os testes unitarios e, por isso, essas inconsistencias qdo eu rodo a suite dos testes.
Mas valeu, se tiver alguma outra sugestao ai para melhorar os testes unitarios, fique a vontade.
Olha como nós estamos usando aqui, não sei se é a melhor prática mas vamos lá
:arrow: a classe de teste não é responsável por trabalhar com transação e nem com controle de sessão do hibernate, estamos usando um service locator para buscar essas coisas, também não usamos setup usamos um getConfigTestCase1 do service locator para trazer tudo que precisamos…
:arrow: a seqüência de testes é feita assim: primeiro eu testo as situações que dão erro usando como entrada dados que simulam o erro, no final eu testo a situação de sucesso usando uma massa de dados que irá dar sucesso para todos as nossas classes
arabéns, você acabou de descobrir como TDD e testes em geral ajudam a pensar em melhorar seu design.
Antes de mais nada, eu li sobre banco de dados ali. Se seu teste está usando banco de dados ele não é um teste unitário, é de integração. Testes de integração devem testar apenas a relação entre dois componentes (“se eu mando pro banco X ele me responde Y”) e não lógica de negócio. Lógica de negócio deve ser testada emt estes unitários, onde cada classe é testada isoladamente (i.e. sem Banco de Dados, sem container, sem nada além do JUnit e uma biblioteca de mocks).
Baseado no seu relato eu creio que seu problema é exatamente que você está tentando testar uma classe que contêm regra de negócios e fala com o banco de dados. O primeiro passo é separar as responsabilidade em duas ou mais classes, assim vcoê vai conseguir escrever testes mais eficientes para regra de negócio e vai simplificar seu teste de interação.
Procure uma biblioteca como o JMock para criar mocks da interação com o banco de dados, por exemplo.
Se o seu teste precisa de x dados do banco de dados, vc pode simular que existem estes x dados e ver se o seu código se comporta conforme o esperado.
Mas para isso vc deve separar bem as responsabilidades sob pena de ter mocks extremamente complexos ou praticamente impossiveis de serem trabalhados.
Bom, a idéia de usar o banco nos testes unitários e não mock objects foi a seguinte:
Primeiro eu implemento os testes unitários relacionados à camada de persistência.
Depois eu implemento as classes de testes unitários relacionados à camada de negócio que eventualmente acessam esses métodos de persistência.
A vantagem que eu vejo é que eu consigo isolar os erros que possam surgir. Se ocorrer um erro em algum teste unitário da camada de negócio e os testes unitários das classes de persistência estiverem OK, então, com certeza o erro está nessa camada de negócios.
Essa é uma forma meio “top-down” de testar as funcionalidades. A idéia de criar mock objects é interessante se eu for começar os testes unitários a partir da camada de negócios e não tiver nenhum teste unitário para validar os métodos de teste para validar as minhas classes de persistência. O problema que eu vejo é que vai dar um trabalhão implementar esses mock objects e de qqer forma, depois eu vou ter que implementar os métodos testes para validar as classes de persistência. Logo, pq não começar validando os métodos de persistência para então validar os de negócio, i.e., sem usar mock objects ?
Realmente, no meu caso específico, se eu tivesse feito os mock objects emulando o comportamento do BD, os meus testes unitários relacionados às classes de negócio teriam passado, entretanto, o problema do reuso da Session do Hibernate iria passar batido.
Em resumo, questiono a real vantagem em se fazer testes unitários usando mock objects, em vez de fazer logo os testes de integração que o Phillip menciona.
Então, aqui a gente também tem uma suite de testes para BD que é rodada antes e depois então rodamos a suite de testes para business.
Eu nunca usei mocks mas já estamos pensando em colocar no projeto
O problema que temos em fazer do jeito que está é que toda vez que formos recriar a base de dados temos que carregar os dados iniciais para que os testes funcionem
Eu acho que isso não é legal, e talvez a gente implemente a solução de usar os mocks, o dbunit eu relamente não conheço
pois eh, aqui no projeto o pessoal montou um script que faz uma carga inicial no BD. E todos os testcases sao baseados nas informacoes contidas nessa carga.
De qqer forma, acho melhor eu estudar mais um pouquinho aqui sobre TDD para chegar a uma conclusão sobre o qual a forma mais interessante de se implementar esses testes.
Hm… tem algo bem estranho ai. Num sistema Orientado a Objetos você espera que o fluxo seja contrário: primeiro os objetos e depois o banco. De qualquer modo, como falei antes, testes que conectam com o banco de dados não são unitários, são testes de integração e devem ser tratados de maneira bem diferente.
Exato, e por isso que você deve separar bem as responsabilidades. De qualquer forma, use os testes de desenvolvedor como ajuda no design e não como verificação. A verificação é um efeito colateral, útil mas ainda um efeito colateral.
A forma que você escreveu não é top-down, ela é extremamente bottom-up. Top-down seria você testar primeiro a funcionalidade toda, provavelmente com um teste de aceitação, depois criar os testes na Camada de Negócios e só depois persistência.
O trabalho repetido que você vai ter se deve ao fato de que suas classes estão fazendo coisas demais. Como disse antes separe suas classe em lógica de negócios (testadas com mocks) e acesso a recursos (testadas em testes de integração).
]Você não deve criar mocks para o banco de dados! Só se cria mocks do seu código.
Acho que você pode não ter entendido o que quis dizer. Testes de Integração são usados em conjunto com Testes Unitários. Os primeiros testam a interação do seu sistema com outros sistemas e recursos e o segundo testa as suas classes de negócio.