Pergunta

Como posso usar JUnit4 idiomaticamente de teste que algum código lança uma exceção?

Enquanto eu certamente pode fazer algo como isto:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }

  assertTrue(thrown);
}

Lembro-me que há uma anotação ou um Assert.xyz ou algo que é muito menos kludgy e muito mais in-the-espírito de JUnit para esses tipos de situações.

Foi útil?

Solução

JUnit 4 tem suporte para o seguinte:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);
}

Referência: https://junit.org/junit4/faq.html#atests_7

Outras dicas

Editar Agora que JUnit5 lançou, a melhor opção seria a utilização Assertions.assertThrows() (ver minha outra resposta ).

Se você não ter migrado para JUnit 5, mas pode usar JUnit 4.7, você pode usar o ExpectedException Regra:

public class FooTest {
  @Rule
  public final ExpectedException exception = ExpectedException.none();

  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    exception.expect(IndexOutOfBoundsException.class);
    foo.doStuff();
  }
}

Este é muito melhor do que @Test(expected=IndexOutOfBoundsException.class) porque o teste falhará se IndexOutOfBoundsException é jogado antes foo.doStuff()

este artigo para mais detalhes

Tenha cuidado ao usar exceção esperado, porque ele só afirma que o método jogou essa exceção, não uma determinada linha de código no teste.

I tendem a usar isso para testar a validação de parâmetros, porque esses métodos são geralmente muito simples, mas testes mais complexos poderiam ser melhor servidos com:

try {
    methodThatShouldThrow();
    fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}

Aplicar julgamento.

Como respondidas antes, há muitas maneiras de lidar com exceções no JUnit. Mas com Java 8 existe outra: usando expressões lambda. Com expressões lambda podemos alcançar uma sintaxe como esta:

@Test
public void verifiesTypeAndMessage() {
    assertThrown(new DummyService()::someMethod)
            .isInstanceOf(RuntimeException.class)
            .hasMessage("Runtime exception occurred")
            .hasMessageStartingWith("Runtime")
            .hasMessageEndingWith("occurred")
            .hasMessageContaining("exception")
            .hasNoCause();
}

assertThrown aceita uma interface funcional, cujas instâncias podem ser criadas com expressões lambda, referências de métodos, ou referências do construtor. assertThrown aceitar essa interface vai esperar e estar pronto para lidar com uma exceção.

Este é relativamente simples, mas poderosa técnica.

Tenha um olhar para este post descrevendo esta técnica: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

O código-fonte pode ser encontrada aqui: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8

Divulgação:. Eu sou o autor do blog e do projeto

Na JUnit, existem quatro maneiras de exceção teste.

  • para junit4.x, use o atributo opcional 'esperado' de teste annonation

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
    
  • para junit4.x, usar a regra ExpectedException

    public class XxxTest {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void testFooThrowsIndexOutOfBoundsException() {
            thrown.expect(IndexOutOfBoundsException.class)
            //you can test the exception message like
            thrown.expectMessage("expected messages");
            foo.doStuff();
        }
    }
    
  • Você também pode usar a maneira clássica try / catch amplamente utilizado sob estrutura JUnit 3

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            fail("expected exception was not occured.");
        } catch(IndexOutOfBoundsException e) {
            //if execution reaches here, 
            //it indicates this exception was occured.
            //so we need not handle it.
        }
    }
    
  • Finalmente, para junit5.x, você também pode usar assertThrows da seguinte forma

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }
    
  • para

    • 1º maneira é usado quando quiser único teste o tipo de exceção
    • as outras três formas são usados ??quando você quiser mensagem de exceção de ensaio
    • Se você usar JUnit 3, então é preferível a 3ª
    • Se você gosta junit 5, então você deve gostar da 4ª
  • Para obter mais informações, você pode ler este documento e junit5 usuário guia para mais detalhes.

tl; dr

  • Pré-JDK8: Vou recomendar o bloco boa try-catch idade. ( Não se esqueça de adicionar uma afirmação fail() antes do bloco catch )

  • pós-JDK8:. Use AssertJ ou costume lambdas para assert excepcional comportamento

Independentemente do JUnit 4 ou JUnit 5.

a longa história

É possível escrever-se um fazê-lo sozinho bloco try-catch ou usar as ferramentas JUnit (@Test(expected = ...) ou o recurso @Rule ExpectedException regra JUnit).

Mas estes maneira não são tão elegante e não se misturam bem legibilidade sábio com outras ferramentas. Além disso ferramentas JUnit tem algumas armadilhas.

  1. O bloco try-catch você tem que escrever o bloco em torno do comportamento testado, e escrever a afirmação no bloco catch, que pode ser bom, mas muitos acham taht este estilo interrompe o fluxo de leitura de um teste. Além disso, você precisa escrever um Assert.fail no final do bloco try caso contrário o teste pode perder um lado das afirmações; PMD , findbugs ou Sonar será capaz de identificar tais questões.

  2. O recurso @Test(expected = ...) é interessante como você pode escrever menos código e, em seguida, escrever este teste é supostamente menos propenso a erros de codificação. Mas ths abordagem está faltando um algumas áreas.

    • Se as necessidades de teste para verificar as coisas adicionais sobre a exceção como a causa ou a mensagem (boas mensagens de exceção são realmente importantes, ter um tipo de exceção precisa pode não ser suficiente).
    • Além disso, como a expectativa é colocado em torno do método, dependendo de como o código testado está escrito, em seguida, a parte errada do código de teste pode lançar a exceção, levando a teste positivo falso e eu não tenho certeza de que < em> PMD , findbugs ou Sonar dará dicas sobre tal código.

      @Test(expected = WantedException.class)
      public void call2_should_throw_a_WantedException__not_call1() {
          // init tested
          tested.call1(); // may throw a WantedException
      
          // call to be actually tested
          tested.call2(); // the call that is supposed to raise an exception
      }
      
  3. A regra ExpectedException é também uma tentativa de corrigir as advertências anteriores, mas ele se sente um pouco estranho para usar como ele usa um estilo expectativa, EasyMock usuários sabe muito bem este modelo. Pode ser conveniente para alguns, mas se você seguir Behavior Driven Development (BDD) ou Organizar Act Assert (AAA) princípios da regra ExpectedException não vai caber naqueles estilo de escrita . Além de que ele pode sofrer do mesmo problema como o como o caminho @Test, dependendo de onde você coloca a expectativa.

    @Rule ExpectedException thrown = ExpectedException.none()
    
    @Test
    public void call2_should_throw_a_WantedException__not_call1() {
        // expectations
        thrown.expect(WantedException.class);
        thrown.expectMessage("boom");
    
        // init tested
        tested.call1(); // may throw a WantedException
    
        // call to be actually tested
        tested.call2(); // the call that is supposed to raise an exception
    }
    

    Mesmo a exceção prevista é colocado antes da instrução de teste, ele quebra o seu fluxo de leitura se os testes siga BDD ou AAA.

    Veja também este href="https://github.com/junit-team/junit4/issues/706#issuecomment-21385116" rel="noreferrer"> questão ExpectedException. JUnit 4,13-beta-2 mesmo deprecates este mecanismo:

    pedido Pull # 1519 : reprovada ExpectedException

    Os Assert.assertThrows método fornece uma maneira mais agradável para a verificação exceções. Além disso, o uso de ExpectedException é passível de erro quando usado com outras regras como TestWatcher porque a ordem das regras é importante nesse caso.

Assim, essas opções acima tem toda a sua carga de advertências, e claramente não imune a erros codificador.

  1. Há um projeto tomei consciência depois de criar esta resposta que parece promissor, é prendedor -exception .

    Como a descrição do projeto diz, deixou uma gravação codificador em uma linha fluente do código de capturaa exceção e oferecer essa exceção para a afirmação mais tarde. E você pode usar qualquer biblioteca afirmação como Hamcrest ou assertJ .

    Um exemplo rápido retirado da home page:

    // given: an empty list
    List myList = new ArrayList();
    
    // when: we try to get the first element of the list
    when(myList).get(1);
    
    // then: we expect an IndexOutOfBoundsException
    then(caughtException())
            .isInstanceOf(IndexOutOfBoundsException.class)
            .hasMessage("Index: 1, Size: 0") 
            .hasNoCause();
    

    Como você pode ver o código é muito simples, você capturar a exceção em uma linha específica, a API then é um apelido que usarão AssertJ APIs (semelhante ao uso assertThat(ex).hasNoCause()...). Em algum momento o projeto contou com FEST-Assert o ancestral de AssertJ . EDIT:. Parece que o projeto está se formando um suporte Java 8 Lambdas

    Atualmente esta biblioteca tem duas deficiências:

    • No momento da redação deste artigo é digno de nota a dizer esta biblioteca é baseada em Mockito 1.x, pois cria um mock do objeto testado por trás da cena. Como Mockito ainda não é atualizado esta biblioteca não pode trabalho com as classes finais ou métodos finais . E mesmo se ele foi baseado em Mockito 2 na versão atual, isso exigiria a declarar um fabricante mundial simulada (inline-mock-maker), algo que não pode o que quiser, pois isso mockmaker tem diferentes inconvenientes que o mockmaker regular.

    • Ela exige ainda uma outra dependência teste.

    Estas questões não se aplica uma vez que a biblioteca irá apoiar lambdas, no entanto, a funcionalidade será duplicada por AssertJ conjunto de ferramentas.

    Levando tudo em conta, se você não quiser usar a ferramenta catch-exceção, vou recomendar o bom velho caminho do bloco try-catch, pelo menos até ao JDK7. E para os usuários JDK 8 você pode preferir usar AssertJ, pois oferece pode mais do que exceções apenas afirmando.

  2. Com a JDK8, lambdas entrar cena do teste, e eles provaram ser uma forma interessante de assert comportamento excepcional. AssertJ foi atualizado para fornecer uma API fluente bom para assert comportamento excepcional.

    E um teste de amostra com AssertJ :

    @Test
    public void test_exception_approach_1() {
        ...
        assertThatExceptionOfType(IOException.class)
                .isThrownBy(() -> someBadIOOperation())
                .withMessage("boom!"); 
    }
    
    @Test
    public void test_exception_approach_2() {
        ...
        assertThatThrownBy(() -> someBadIOOperation())
                .isInstanceOf(Exception.class)
                .hasMessageContaining("boom");
    }
    
    @Test
    public void test_exception_approach_3() {
        ...
        // when
        Throwable thrown = catchThrowable(() -> someBadIOOperation());
    
        // then
        assertThat(thrown).isInstanceOf(Exception.class)
                          .hasMessageContaining("boom");
    }
    
  3. Com uma reescrita quase completa do JUnit 5, afirmações foram melhorou um pouco, eles podem ser interessante como um fora do caminho caixa para afirmar corretamente exceção. Mas realmente a API afirmação ainda é um pouco pobre, não há nada fora do assertThrows .

    @Test
    @DisplayName("throws EmptyStackException when peeked")
    void throwsExceptionWhenPeeked() {
        Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek());
    
        Assertions.assertEquals("...", t.getMessage());
    }
    

    Como você percebeu assertEquals ainda está retornando void, e como tal não permite o encadeamento de afirmações como AssertJ.

    Além disso, se você se lembra confronto nome com Matcher ou Assert, estar preparado para atender o mesmo choque com Assertions.

Eu gostaria de concluir que hoje (2017/03/03) AssertJ 's facilidade de uso, API detectável, o ritmo acelerado de desenvolvimento e como um de facto teste de dependência é a melhor solução com JDK8 independentemente da estrutura de teste (JUnit ou não), JDKs anteriores devem sim contar com try-catch blocos mesmo que se sintam desajeitado.

Esta resposta foi copiado de outra pergunta que não tem a mesma visibilidade, eu sou o mesmo autor.

Agora que JUnit 5 lançou, a melhor opção é usar Assertions.assertThrows() (ver JUnit 5 Guia do Usuário ).

Aqui está um exemplo que verifica uma exceção é lançada, e usos Verdade para fazer afirmações sobre o mensagem de exceção:

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    IndexOutOfBoundsException e = assertThrows(
        IndexOutOfBoundsException.class, foo::doStuff);

    assertThat(e).hasMessageThat().contains("woops!");
  }
}

As vantagens sobre o aborda em outras respostas são:

  1. Construído em JUnit
  2. Você recebe uma mensagem de exceção útil se o código no lambda não lançar uma exceção, e um stacktrace se lança uma exceção diferente
  3. Concise
  4. Permite que os testes a seguir Organize-Act-Assert
  5. Você pode precisamente indicar o código que você está esperando para lançar a exceção
  6. Você não precisa listar a exceção prevista na cláusula throws
  7. Você pode usar a estrutura de afirmação de sua escolha para fazer afirmações sobre a exceção capturada

Um método semelhante será adicionado à org.junit Assert no JUnit 4.13.

Como sobre este: capturar uma exceção muito geral, certifique-se que a torna fora do bloco catch, em seguida, afirmar que a classe da exceção é o que você espera que ele seja. Este assert falhará se a) a exceção é do tipo errado (por exemplo. Se você tem um ponteiro nulo em vez) e b) a exceção não foi já lançada.

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

  try {
    foo.doStuff();
  } catch (Throwable ex) {
    e = ex;
  }

  assertTrue(e instanceof IndexOutOfBoundsException);
}

BDD Estilo Solução: JUnit 4 + captura Exceção + AssertJ

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(foo).doStuff();

    then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);

}

O código-fonte

Dependências

eu.codearte.catch-exception:catch-exception:1.3.3

Usando uma afirmação AssertJ , que pode ser usado juntamente com JUnit:

import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

É melhor do que @Test(expected=IndexOutOfBoundsException.class) porque garante a linha esperado no teste jogou a exceção e permite verificar mais detalhes sobre a exceção, como mensagem, mais fácil:

assertThatThrownBy(() ->
       {
         throw new Exception("boom!");
       })
    .isInstanceOf(Exception.class)
    .hasMessageContaining("boom");

Maven / Gradle instruções aqui.

Para resolver o mesmo problema que eu tenha criado um pequeno projeto: http://code.google.com/p/catch-exception/

Usando este pequeno ajudante que iria escrever

verifyException(foo, IndexOutOfBoundsException.class).doStuff();

Este é menos detalhada do que a regra ExpectedException de JUnit 4.7. Em comparação com a solução fornecida pela skaffman, você pode especificar em que linha de código que você espera a exceção. Espero que isso ajude.

Update: JUnit5 tem uma melhoria para exceções de testes:. assertThrows

seguindo o exemplo é de: JUnit 5 Guia do Usuário

 @Test
void exceptionTesting() {
    Throwable exception = assertThrows(IllegalArgumentException.class, () -> 
    {
        throw new IllegalArgumentException("a message");
    });
    assertEquals("a message", exception.getMessage());
}

resposta original usando JUnit 4.

Existem várias maneiras de testar se uma exceção é lançada. Eu também têm discutido a seguir opções no meu post Como escrever grandes testes unitários com JUnit

Defina o parâmetro expected @Test(expected = FileNotFoundException.class).

@Test(expected = FileNotFoundException.class) 
public void testReadFile() { 
    myClass.readFile("test.txt");
}

Usando try catch

public void testReadFile() { 
    try {
        myClass.readFile("test.txt");
        fail("Expected a FileNotFoundException to be thrown");
    } catch (FileNotFoundException e) {
        assertThat(e.getMessage(), is("The file test.txt does not exist!"));
    }

}

Os testes com a Regra ExpectedException.

@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void testReadFile() throws FileNotFoundException {

    thrown.expect(FileNotFoundException.class);
    thrown.expectMessage(startsWith("The file test.txt"));
    myClass.readFile("test.txt");
}

Você pode ler mais sobre exceções testes em JUnit4 wiki para Exceção de teste bad.robot - Esperando Exceções JUnit Regra .

Você também pode fazer isso:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
    try {
        foo.doStuff();
        assert false;
    } catch (IndexOutOfBoundsException e) {
        assert true;
    }
}

IMHO, a melhor maneira de verificar se há exceções em JUnit é o try / catch / falha / assert padrão:

// this try block should be as small as possible,
// as you want to make sure you only catch exceptions from your code
try {
    sut.doThing();
    fail(); // fail if this does not throw any exception
} catch(MyException e) { // only catch the exception you expect,
                         // otherwise you may catch an exception for a dependency unexpectedly
    // a strong assertion on the message, 
    // in case the exception comes from anywhere an unexpected line of code,
    // especially important if your checking IllegalArgumentExceptions
    assertEquals("the message I get", e.getMessage()); 
}

O assertTrue pode ser um pouco forte para algumas pessoas, por isso assertThat(e.getMessage(), containsString("the message"); pode ser preferível.

JUnit 5 Solução

@Test
void testFooThrowsIndexOutOfBoundsException() {    
  Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );

  assertEquals( "some message", exception.getMessage() );
}

Mais informações sobre JUnit 5 sobre http://junit.org/junit5/ docs / current / user-guia / # escrita-testes-afirmações

Eu tentei muitos dos métodos aqui, mas eles foram ou complicado ou não satisfazer as minhas necessidades. Na verdade, pode-se escrever um método auxiliar muito simplesmente:

public class ExceptionAssertions {
    public static void assertException(BlastContainer blastContainer ) {
        boolean caughtException = false;
        try {
            blastContainer.test();
        } catch( Exception e ) {
            caughtException = true;
        }
        if( !caughtException ) {
            throw new AssertionFailedError("exception expected to be thrown, but was not");
        }
    }
    public static interface BlastContainer {
        public void test() throws Exception;
    }
}

Use-o assim:

assertException(new BlastContainer() {
    @Override
    public void test() throws Exception {
        doSomethingThatShouldExceptHere();
    }
});

Zero dependências: não há necessidade de Mockito, não há necessidade PowerMock; e funciona muito bem com as classes finais.

A resposta mais flexível e elegante para JUnit 4 que encontrei no Mkyong blogue . Tem a flexibilidade do try/catch usando a anotação @Rule. Eu gosto desta abordagem, porque você pode ler atributos específicos de uma exceção personalizada.

package com.mkyong;

import com.mkyong.examples.CustomerService;
import com.mkyong.examples.exception.NameNotFoundException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;

public class Exception3Test {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void testNameNotFoundException() throws NameNotFoundException {

        //test specific type of exception
        thrown.expect(NameNotFoundException.class);

        //test message
        thrown.expectMessage(is("Name is empty!"));

        //test detail
        thrown.expect(hasProperty("errCode"));  //make sure getters n setters are defined.
        thrown.expect(hasProperty("errCode", is(666)));

        CustomerService cust = new CustomerService();
        cust.findByName("");

    }

}

Java 8 solução

Se você gostaria de uma solução que:

  • Utiliza Java 8 lambdas
  • O não depender de nenhuma mágica JUnit
  • Permite-lhe verificar se há várias exceções dentro de um único método de teste
  • Verifica se há uma exceção a ser lançada por um conjunto específico de linhas dentro do seu método de teste em vez de qualquer linha desconhecida em todo o método de teste
  • Retorna o objeto de exceção real que foi jogado para que você possa examinar melhor it

Aqui está uma função de utilidade que eu escrevi:

public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
    try
    {
        runnable.run();
    }
    catch( Throwable throwable )
    {
        if( throwable instanceof AssertionError && throwable.getCause() != null )
            throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
        assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
        assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
        @SuppressWarnings( "unchecked" )
        T result = (T)throwable;
        return result;
    }
    assert false; //expected exception was not thrown.
    return null; //to keep the compiler happy.
}

( tirado meu blog )

Use-o como segue:

@Test
public void testThrows()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            throw new RuntimeException( "fail!" );
        } );
    assert e.getMessage().equals( "fail!" );
}

JUnit tem suporte embutido para isso, com uma "esperado" atributo

No meu caso eu sempre obter RuntimeException de db, mas as mensagens são diferentes. E necessidade exceção a ser tratada, respectivamente. Aqui está como eu testei:

@Test
public void testThrowsExceptionWhenWrongSku() {

    // Given
    String articleSimpleSku = "999-999";
    int amountOfTransactions = 1;
    Exception exception = null;

    // When
    try {
        createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
    } catch (RuntimeException e) {
        exception = e;
    }

    // Then
    shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
}

private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
    assertNotNull(e);
    assertTrue(e.getMessage().contains(message));
}

Basta fazer um Matcher que pode ser desligado e ligado, como este:

public class ExceptionMatcher extends BaseMatcher<Throwable> {
    private boolean active = true;
    private Class<? extends Throwable> throwable;

    public ExceptionMatcher(Class<? extends Throwable> throwable) {
        this.throwable = throwable;
    }

    public void on() {
        this.active = true;
    }

    public void off() {
        this.active = false;
    }

    @Override
    public boolean matches(Object object) {
        return active && throwable.isAssignableFrom(object.getClass());
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("not the covered exception type");
    }
}

Para usá-lo:

public ExpectedException exception = ExpectedException.none(); add, em seguida:

ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();

Na JUnit 4 ou mais tarde você pode testar as exceções da seguinte maneira

@Rule
public ExpectedException exceptions = ExpectedException.none();


isso proporciona uma grande quantidade de recursos que podem ser usados ??para melhorar os nossos testes JUnit.
Se você ver o exemplo abaixo estou testando 3 coisas sobre a exceção.

  1. O tipo de exceção lançada
  2. A mensagem de exceção
  3. A causa da exceção


public class MyTest {

    @Rule
    public ExpectedException exceptions = ExpectedException.none();

    ClassUnderTest classUnderTest;

    @Before
    public void setUp() throws Exception {
        classUnderTest = new ClassUnderTest();
    }

    @Test
    public void testAppleisSweetAndRed() throws Exception {

        exceptions.expect(Exception.class);
        exceptions.expectMessage("this is the exception message");
        exceptions.expectCause(Matchers.<Throwable>equalTo(exceptionCause));

        classUnderTest.methodUnderTest("param1", "param2");
    }

}

Podemos usar uma afirmação falhar após o método que deve retornar uma exceção:

try{
   methodThatThrowMyException();
   Assert.fail("MyException is not thrown !");
} catch (final Exception exception) {
   // Verify if the thrown exception is instance of MyException, otherwise throws an assert failure
   assertTrue(exception instanceof MyException, "An exception other than MyException is thrown !");
   // In case of verifying the error message
   MyException myException = (MyException) exception;
   assertEquals("EXPECTED ERROR MESSAGE", myException.getMessage());
}

Adicionalmente ao que NamShubWriter disse, certifique-se de que:

  • A instância ExpectedException é public ( Pergunta relacionada )
  • O ExpectedException não é instanciado em dizer, o método @Before. Este pós explica claramente todos os meandros da ordem de execução do JUnit.

não fazer isso:

@Rule    
public ExpectedException expectedException;

@Before
public void setup()
{
    expectedException = ExpectedException.none();
}

Finalmente, este post ilustra claramente como afirmar que uma certa exceção é lançada.

Eu recomendo assertj-core biblioteca a exceção pega no teste JUnit

Em java 8, como este:

//given

//when
Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));

//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);

solução Junit4 com Java8 é usar esta função:

public Throwable assertThrows(Class<? extends Throwable> expectedException, java.util.concurrent.Callable<?> funky) {
    try {
        funky.call();
    } catch (Throwable e) {
        if (expectedException.isInstance(e)) {
            return e;
        }
        throw new AssertionError(
                String.format("Expected [%s] to be thrown, but was [%s]", expectedException, e));
    }
    throw new AssertionError(
            String.format("Expected [%s] to be thrown, but nothing was thrown.", expectedException));
}

O uso é então:

    assertThrows(ValidationException.class,
            () -> finalObject.checkSomething(null));

Note-se que a única limitação é a utilização de uma referência final objecto na expressão lambda. Esta solução permite continuar afirmações de teste em vez de esperar thowable a nível método utilizando solução @Test(expected = IndexOutOfBoundsException.class).

Tomemos por exemplo, você quer escrever JUnit para abaixo código mencionado fragmento

public int divideByZeroDemo(int a,int b){

    return a/b;
}

public void exceptionWithMessage(String [] arr){

    throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}

O código acima é para testar alguma exceção desconhecida que pode ocorrer ea seguir é para afirmar alguma exceção com mensagem personalizada.

 @Rule
public ExpectedException exception=ExpectedException.none();

private Demo demo;
@Before
public void setup(){

    demo=new Demo();
}
@Test(expected=ArithmeticException.class)
public void testIfItThrowsAnyException() {

    demo.divideByZeroDemo(5, 0);

}

@Test
public void testExceptionWithMessage(){


    exception.expectMessage("Array is out of bound");
    exception.expect(ArrayIndexOutOfBoundsException.class);
    demo.exceptionWithMessage(new String[]{"This","is","a","demo"});
}

Com Java 8 você pode criar um método de tomar um código para verificar e exceção esperada como parâmetros:

private void expectException(Runnable r, Class<?> clazz) { 
    try {
      r.run();
      fail("Expected: " + clazz.getSimpleName() + " but not thrown");
    } catch (Exception e) {
      if (!clazz.isInstance(e)) fail("Expected: " + clazz.getSimpleName() + " but " + e.getClass().getSimpleName() + " found", e);
    }
  }

e, em seguida, dentro do seu teste:

expectException(() -> list.sublist(0, 2).get(2), IndexOutOfBoundsException.class);

Benefícios:

  • não depender de qualquer biblioteca
  • localizada check - mais preciso e permite ter múltiplas afirmações como esta dentro de um teste, se necessário
  • fácil de usar

A minha solução usando Java 8 lambdas:

public static <T extends Throwable> T assertThrows(Class<T> expected, ThrowingRunnable action) throws Throwable {
    try {
        action.run();
        Assert.fail("Did not throw expected " + expected.getSimpleName());
        return null; // never actually
    } catch (Throwable actual) {
        if (!expected.isAssignableFrom(actual.getClass())) { // runtime '!(actual instanceof expected)'
            System.err.println("Threw " + actual.getClass().getSimpleName() 
                               + ", which is not a subtype of expected " 
                               + expected.getSimpleName());
            throw actual; // throw the unexpected Throwable for maximum transparency
        } else {
            return (T) actual; // return the expected Throwable for further examination
        }
    }
}

Você tem que definir um FunctionalInterface, porque Runnable não declarar a throws necessário.

@FunctionalInterface
public interface ThrowingRunnable {
    void run() throws Throwable;
}

O método pode ser utilizado como se segue:

class CustomException extends Exception {
    public final String message;
    public CustomException(final String message) { this.message = message;}
}
CustomException e = assertThrows(CustomException.class, () -> {
    throw new CustomException("Lorem Ipsum");
});
assertEquals("Lorem Ipsum", e.message);

Existem duas maneiras de caso escrito teste

  1. Anotar o teste com a exceção que é lançada pelo método. Algo como isso @Test(expected = IndexOutOfBoundsException.class)
  2. Você pode simplesmente capturar a exceção na classe de teste usando o bloco try catch e assert na mensagem que é lançada a partir do método na classe de teste.

    try{
    }
    catch(exception to be thrown from method e)
    {
         assertEquals("message", e.getmessage());
    }
    

Espero que isso responde sua consulta aprendizagem feliz ...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top