Guia de Regras da JUnit 4

Guia de Regras da JUnit 4

1. Visão geral

Neste tutorial, vamos dar uma olhada no recurso Regras fornecido pela bibliotecaJUnit 4.

Começaremos apresentando o modelo de regras JUnit antes de percorrer as regras básicas mais importantes fornecidas pela distribuição. Additionally, we’ll also see how to write and use our own custom JUnit Rule.

Para aprender mais sobre como testar o JUnit, verifique nosso abrangenteJUnit series.

Observe que se você estiver usando o JUnit 5, as regras foram substituídas porExtension model.

2. Introdução às regras JUnit 4

JUnit 4 rules provide a flexible mechanism to enhance tests by running some code around a test case execution. Em certo sentido, é semelhante a ter@Before and @After annotations em nossa classe de teste.

Vamos imaginar que queremos nos conectar a um recurso externo, como um banco de dados, durante a configuração do teste e, em seguida, fechar a conexão após o término do nosso teste. Se quisermos usar esse banco de dados em vários testes, acabaremos duplicando esse código em todos os testes.

Usando uma regra, podemos ter tudo isolado em um lugar e reutilizar o código facilmente de várias classes de teste.

3. Usando regras JUnit 4

Então, como podemos usar regras? Podemos usar as regras do JUnit 4 seguindo estas etapas simples:

  • Adicione um campopublic à nossa classe de teste e certifique-se de que o tipo deste campo é um subtipo da interfaceorg.junit.rules.TestRule

  • Anote o campo com a anotação@Rule

Na próxima seção, veremos quais dependências do projeto precisamos para começar.

4. Dependências do Maven

Primeiro, vamos adicionar as dependências do projeto de que precisaremos para nossos exemplos. Precisamos apenas da biblioteca principal JUnit 4:


    junit
    junit
    4.12

Como sempre, podemos obter a versão mais recente deMaven Central.

5. Regras fornecidas na distribuição

Claro,JUnit provides a number of useful, predefined rules as part of the library. Podemos encontrar todas essas regras no pacoteorg.junit.rules.

Nesta seção, veremos alguns exemplos de como usá-los.

5.1. A regraTemporaryFolder

Ao testar, geralmente precisamos acessar um arquivo ou pasta temporário. No entanto, o gerenciamento da criação e exclusão desses arquivos pode ser complicado. Using the TemporaryFolder rule, we can manage the creation of files and folders that should be deleted when the test method terminates:

@Rule
public TemporaryFolder tmpFolder = new TemporaryFolder();

@Test
public void givenTempFolderRule_whenNewFile_thenFileIsCreated() throws IOException {
    File testFile = tmpFolder.newFile("test-file.txt");

    assertTrue("The file should have been created: ", testFile.isFile());
    assertEquals("Temp folder and test file should match: ",
      tmpFolder.getRoot(), testFile.getParentFile());
}

Como podemos ver, primeiro definimos a regraTemporaryFoldertmpFolder. A seguir, nosso método de teste cria um arquivo chamadotest-file.txt na pasta temporária. Em seguida, verificamos se o arquivo foi criado e existe onde deveria. Muito bom e simples!

Quando o teste terminar, a pasta e o arquivo temporários deverão ser excluídos. However, this rule doesn’t check whether or not the deletion is successful.

Existem também alguns outros métodos interessantes que vale a pena mencionar nesta classe:

  • newFile()

    Se não fornecermos nenhum nome de arquivo, este método criará um novo arquivo nomeado aleatoriamente.

  • newFolder(String... folderNames)

    Para criar pastas temporárias recursivamente profundas, podemos usar este método.

  • newFolder()

    Da mesma forma, o métodonewFolder() cria uma nova pasta nomeada aleatoriamente.

Uma boa adição que vale a pena mencionar é que a partir da versão 4.13, a regraTemporaryFolder permite a verificação de recursos excluídos:

@Rule
public TemporaryFolder folder = TemporaryFolder.builder().assureDeletion().build();

Se um recurso não puder ser excluído, o teste com falha comAssertionError.

Finalmente, emJUnit 5, podemos obter a mesma funcionalidade usandoTemporary Directory extension.

5.2. A regraExpectedException

Como o nome sugere,we can use the ExpectedException rule to verify that some code throws an expected exception:

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

@Test
public void givenIllegalArgument_whenExceptionThrown_MessageAndCauseMatches() {
    thrown.expect(IllegalArgumentException.class);
    thrown.expectCause(isA(NullPointerException.class));
    thrown.expectMessage("This is illegal");

    throw new IllegalArgumentException("This is illegal", new NullPointerException());
}

Como podemos ver no exemplo acima, primeiro declaramos a regraExpectedException. Então, em nosso teste, estamos afirmando que umIllegalArgumentException é lançado.

Usando essa regra, também podemos verificar algumas outras propriedades da exceção, como a mensagem e a causa.

Para obter um guia detalhado para testar exceções com JUnit, verifique nosso excelente guia sobre comoAssert an Exception.

5.3. A regraTestName

Simplificando, a regraTestName fornece o nome do teste atual dentro de um determinado método de teste:

@Rule public TestName name = new TestName();

@Test
public void givenAddition_whenPrintingTestName_thenTestNameIsDisplayed() {
    LOG.info("Executing: {}", name.getMethodName());
    assertEquals("givenAddition_whenPrintingTestName_thenTestNameIsDisplayed", name.getMethodName());
}

Neste exemplo trivial, quando executamos o teste de unidade, devemos ver o nome do teste na saída:

INFO  c.example.rules.JUnitRulesUnitTest -
    Executing: givenAddition_whenPrintingTestName_thenTestNameIsDisplayed

5.4. A regraTimeout

Neste próximo exemplo, vamos dar uma olhada na regraTimeout. This rule offers a useful alternative to using the timeout parameter on an individual Test annotation.

Agora, vamos ver como usar essa regra para definir um tempo limite global em todos os métodos de teste em nossa classe de teste:

@Rule
public Timeout globalTimeout = Timeout.seconds(10);

@Test
public void givenLongRunningTest_whenTimout_thenTestFails() throws InterruptedException {
    TimeUnit.SECONDS.sleep(20);
}

No exemplo trivial acima,we first define a global timeout for all test methods of 10 seconds. Em seguida, definimos deliberadamente um teste que levará mais de 10 segundos.

Quando executamos este teste, devemos ver uma falha no teste:

org.junit.runners.model.TestTimedOutException: test timed out after 10 seconds
...

5.5. A regraErrorCollector

A seguir, vamos dar uma olhada na regraErrorCollector. This rule allows the execution of a test to continue after the first problem is found.

Vamos ver como podemos usar essa regra para coletar todos os erros e relatá-los todos de uma vez quando o teste terminar:

@Rule
public final ErrorCollector errorCollector = new ErrorCollector();

@Test
public void givenMultipleErrors_whenTestRuns_thenCollectorReportsErrors() {
    errorCollector.addError(new Throwable("First thing went wrong!"));
    errorCollector.addError(new Throwable("Another thing went wrong!"));

    errorCollector.checkThat("Hello World", not(containsString("ERROR!")));
}

No exemplo acima, adicionamos dois erros ao coletor. When we run the test, the execution continues, but the test will fail at the end.

Na saída, veremos os dois erros relatados:

java.lang.Throwable: First thing went wrong!
...
java.lang.Throwable: Another thing went wrong!

5.6. A regraVerifier

The Verifier rule is an abstract base class that we can use when we wish to verify some additional behavior from our tests. Na verdade, a regraErrorCollector que vimos na última seção estende essa classe.

Vamos agora dar uma olhada em um exemplo trivial de definição de nosso próprio verificador:

private List messageLog = new ArrayList();

@Rule
public Verifier verifier = new Verifier() {
    @Override
    public void verify() {
        assertFalse("Message Log is not Empty!", messageLog.isEmpty());
    }
};

Aqui, definimos um novoVerifiere substituímos o métodoverify() para adicionar alguma lógica de verificação extra. Neste exemplo simples, simplesmente verificamos se o registro de mensagens em nosso exemplo não está vazio.

Agora, quando executamos o teste de unidade e adicionamos uma mensagem, devemos ver que nosso verificador foi aplicado:

@Test
public void givenNewMessage_whenVerified_thenMessageLogNotEmpty() {
    // ...
    messageLog.add("There is a new message!");
}

5.7. A regraDisableOnDebug

Sometimes we may want to disable a rule when we’re debugging. Por exemplo, muitas vezes é desejável desabilitar uma regraTimeout durante a depuração para evitar que nosso teste expire e falhe antes de termos tempo para depurá-lo adequadamente.

A regraDisableOnDebug faz exatamente isso e nos permite rotular certas regras para serem desabilitadas durante a depuração:

@Rule
public DisableOnDebug disableTimeout = new DisableOnDebug(Timeout.seconds(30));

No exemplo acima, podemos ver que, para usar esta regra,we simply pass the rule we want to disable to the constructor.

O principal benefício desta regra é que podemos desabilitar as regras sem fazer nenhuma modificação em nossas classes de teste durante a depuração.

5.8. A regraExternalResource

Normalmente, ao escrever testes de integração, podemos desejar configurar um recurso externo antes de um teste e destruí-lo posteriormente. Felizmente, o JUnit fornece outra classe básica útil para isso.

We can extend the abstract class ExternalResource to set up an external resource before a test, such as a file or a database connection. Na verdade, a regraTemporaryFolder que vimos anteriormente estendeExternalResource.

Vamos dar uma olhada rápida em como podemos estender esta classe:

@Rule
public final ExternalResource externalResource = new ExternalResource() {
    @Override
    protected void before() throws Throwable {
        // code to set up a specific external resource.
    };

    @Override
    protected void after() {
        // code to tear down the external resource
    };
};

Neste exemplo, quando definimos um recurso externo, simplesmente precisamos substituir o métodobefore()e o métodoafter() para configurar e destruir nosso recurso externo.

6. Aplicando Regras de Classe

Até agora, todos os exemplos que vimos se aplicavam a métodos de caso de teste único. However, sometimes we might want to apply a rule at the test class level. Podemos fazer isso usando a anotação@ClassRule.

Esta anotação funciona de forma muito semelhante a@Rule, mas envolve uma regra em torno de um teste inteiro -the main difference being that the field we use for our class rule must be static:

@ClassRule
public static TemporaryFolder globalFolder = new TemporaryFolder();

7. Definindo uma regra JUnit personalizada

Como vimos, o JUnit 4 fornece uma série de regras úteis prontas para usar. Obviamente, podemos definir nossas próprias regras personalizadas. To write a custom rule, we need to implement the TestRule interface.

Vamos dar uma olhada em um exemplo de definição de uma regra de logger de nome de método de teste personalizado:

public class TestMethodNameLogger implements TestRule {

    private static final Logger LOG = LoggerFactory.getLogger(TestMethodNameLogger.class);

    @Override
    public Statement apply(Statement base, Description description) {
        logInfo("Before test", description);
        try {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    base.evaluate();
                }
            };
        } finally {
            logInfo("After test", description);
        }
    }

    private void logInfo(String msg, Description description) {
        LOG.info(msg + description.getMethodName());
    }
}

Como podemos ver, a interfaceTestRule contém um método chamadoapply(Statement, Description) que devemos substituir para retornar uma instância deStatement. A instrução representa nossos testes no tempo de execução JUnit. When we call the evaluate() method, this executes our test.

Neste exemplo, registramos uma mensagem antes e depois e incluímos do objetoDescription o nome do método do teste individual.

8. Usando cadeias de regras

Nesta seção final, daremos uma olhada em como podemos ordenar várias regras de teste usando a regraRuleChain:

@Rule
public RuleChain chain = RuleChain.outerRule(new MessageLogger("First rule"))
    .around(new MessageLogger("Second rule"))
    .around(new MessageLogger("Third rule"));

No exemplo acima, criamos uma cadeia de três regras que simplesmente imprimem a mensagem passada para cada construtorMessageLogger.

Quando executarmos nosso teste, veremos como a cadeia é aplicada em ordem:

Starting: First rule
Starting: Second rule
Starting: Third rule
Finished: Third rule
Finished: Second rule
Finished: First rule

9. Conclusão

Para resumir, neste tutorial, exploramos as regras da JUnit 4 em detalhes.

Primeiro, começamos explicando o que são regras e como podemos usá-las. Em seguida, examinamos detalhadamente as regras que fazem parte da distribuição JUnit.

Finalmente, vimos como podemos definir nossa própria regra personalizada e como encadear regras.

Como sempre, o código-fonte completo do artigo está disponívelover on GitHub.