Eu sei que uma forma de o fazer seria
@Test
public void foo(){
try{
//execute code that you expect not to throw Exceptions.
}
catch(Exception e){
fail("Should not have thrown any exception");
}
}
Haverá alguma forma mais limpa de o fazer? (Provavelmente utilizando Junit's @Rule
?)
Você'está a abordar isto da forma errada. Basta testar a sua funcionalidade: se for lançada uma excepção, o teste falhará automaticamente. Se não for lançada uma excepção, todos os seus testes ficarão verdes.
Tenho notado que esta questão suscita interesse de tempos a tempos, por isso I'irá expandir-se um pouco.
Quando você're unidade de teste'é importante definir para si próprio o que você considera uma unidade de trabalho. Basicamente: uma extracção da sua base de códigos que pode ou não incluir múltiplos métodos ou classes que representam uma única peça de funcionalidade.
Ou, como definido em The art of Unit Testing, 2nd Edition by Roy Osherove, página 11:
Um teste de unidade é um código automatizado que invoca a unidade de trabalho a ser testada, e depois verifica algumas suposições sobre um único resultado final dessa unidade. Um teste de unidade é quase sempre escrito utilizando uma estrutura de teste de unidade. Pode ser escrito facilmente e executado rapidamente. É digno de confiança, legível, e de manutenção. It's consistente nos seus resultados desde que o código de produção não't tenha mudado.
O que é importante perceber é que uma unidade de trabalho é geralmente'não é apenas um método mas ao nível muito básico é um método e depois é encapsulado por outra unidade de trabalho.
O ideal seria ter um método de teste para cada unidade de trabalho separada, para que possa sempre ver imediatamente onde as coisas estão a correr mal. Neste exemplo há um método básico chamado getUserById()
que devolverá um utilizador e há um total de 3 unidades de trabalho.
A primeira unidade de trabalho deve testar se um utilizador válido está ou não a ser devolvido no caso de entrada válida e inválida.
Quaisquer excepções que estejam a ser lançadas pela fonte de dados têm de ser tratadas aqui: se nenhum utilizador estiver presente, deve haver um teste que demonstre que é lançada uma excepção quando o utilizador pode't ser encontrado. Uma amostra disto poderia ser a "IllegalArgumentException" que é apanhada com a anotação @Test(expected = IllegalArgumentException.class)
.
Depois de ter tratado de todas as suas invenções para esta unidade básica de trabalho, passa a um nível superior. Aqui faz exactamente o mesmo, mas só trata das excepções que vêm do nível imediatamente abaixo do actual. Isto mantém o seu código de teste bem estruturado e permite-lhe correr rapidamente pela arquitectura para encontrar onde as coisas correm mal, em vez de ter de saltar por todo o lado.
Neste momento, deve ficar claro como nós'vamos lidar com estas excepções. Existem 2 tipos de entrada: entrada valida e falsa (a entrada é válida no sentido estrito, mas não é's não está correcta).
Quando trabalha com valid, introduza-o'reajusta a expectativa implícita de que qualquer teste que escreva, irá funcionar.
Tal chamada de método pode ter este aspecto: existingUserById_ShouldReturn_UserObject
. Se este método falhar (por exemplo: é lançada uma excepção), então sabe que algo correu mal e pode começar a cavar.
Ao adicionar outro teste (`nonExistingUserById_ShouldThrow_IllegalArgumentException') que utiliza a entrada faulty e espera uma excepção, pode ver se o seu método faz o que é suposto fazer com a entrada errada.
Estava a tentar fazer duas coisas no seu teste: verificar se a entrada de dados era válida e defeituosa. Dividindo isto em dois métodos para que cada um faça uma coisa, terá testes muito mais claros e uma visão muito melhor de onde as coisas correm mal.
Mantendo a unidade de camadas de trabalhos em mente, pode também reduzir a quantidade de testes necessários para uma camada superior na hierarquia porque não'não tem de prestar contas de tudo o que possa ter corrido mal nas camadas inferiores: as camadas abaixo da actual são uma garantia virtual de que as suas dependências funcionam e, se algo correr mal,'s na sua camada actual (assumindo que as camadas inferiores não'não atirem quaisquer erros em si).
Se tiver o azar de apanhar todos os erros no seu código. Pode estupidamente fazer
class DumpTest {
Exception ex;
@Test
public void testWhatEver() {
try {
thisShouldThroughError();
} catch (Exception e) {
ex = e;
}
assertEquals(null,ex);
}
}
Se quiser testar se o seu alvo de teste consome a excepção. Basta deixar o teste como (colaborador falso usando jMock2):
@Test
public void consumesAndLogsExceptions() throws Exception {
context.checking(new Expectations() {
{
oneOf(collaborator).doSth();
will(throwException(new NullPointerException()));
}
});
target.doSth();
}
O teste passaria se o seu alvo consumisse a excepção lançada, caso contrário o teste falharia.
Se quiser testar a sua lógica de consumo de excepção, as coisas tornam-se mais complexas. Sugiro que se delegue o consumo a um colaborador que poderia ser ridicularizado. Portanto, o teste poderia ser:
@Test
public void consumesAndLogsExceptions() throws Exception {
Exception e = new NullPointerException();
context.checking(new Expectations() {
{
allowing(collaborator).doSth();
will(throwException(e));
oneOf(consumer).consume(e);
}
});
target.doSth();
}
Mas por vezes é's excessivamente concebido se apenas o quiser registar. Neste caso, este artigo (http://java.dzone.com/articles/monitoring-declarative-transac, http://blog.novoj.net/2008/09/20/testing-aspect-pointcuts-is-there-an-easy-way/) pode ajudar se insistir em tdd neste caso.