Antes e depois do gancho de execução do Suite no jUnit 4.x

84

Estou tentando configurar e desmontar um conjunto de testes de integração, usando o jUnit 4.4 para executar os testes. A desmontagem deve ser executada de forma confiável. Estou tendo outros problemas com o TestNG, por isso estou tentando fazer a portabilidade de volta para o jUnit. Quais ganchos estão disponíveis para execução antes que qualquer teste seja executado e depois que todos os testes forem concluídos?

Nota: estamos usando o maven 2 para nossa construção. Eu tentei usar as pre-& post-integration-testfases do maven , mas, se um teste falhar, o maven para e não executa post-integration-test, o que não ajuda em nada.

sblundy
fonte
1
Para testes de integração, você deve usar o plugin maven-failafe em vez do surefire. Isso não será ignorado post-integration-testse um teste falhar. Veja também esta página wiki .
Chris H.
você pode compartilhar sua implementação final, por favor?
vikramvi,

Respostas:

114

Sim, é possível executar métodos de configuração e desmontagem de maneira confiável antes e depois de qualquer teste em uma suíte de teste. Deixe-me demonstrar em código:

package com.test;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({Test1.class, Test2.class})
public class TestSuite {

    @BeforeClass
    public static void setUp() {
        System.out.println("setting up");
    }

    @AfterClass
    public static void tearDown() {
        System.out.println("tearing down");
    }

}

Portanto, sua Test1classe seria algo como:

package com.test;

import org.junit.Test;


public class Test1 {
    @Test
    public void test1() {
        System.out.println("test1");
    }

}

... e você pode imaginar que Test2é semelhante. Se você corresse TestSuite, obteria:

setting up
test1
test2
tearing down

Portanto, você pode ver que a configuração / desmontagem só é executada antes e depois de todos os testes, respectivamente.

O problema: isso só funciona se você estiver executando o conjunto de testes e não executando Test1 e Test2 como testes JUnit individuais. Você mencionou que está usando o maven, e o plug-in surefire do maven gosta de executar testes individualmente, e não como parte de um pacote. Nesse caso, eu recomendaria criar uma superclasse que cada classe de teste estende. A superclasse então contém os métodos @BeforeClass e @AfterClass anotados. Embora não seja tão limpo quanto o método acima, acho que funcionará para você.

Quanto ao problema com testes com falha, você pode definir maven.test.error.ignore para que a construção continue nos testes que falharam. Isso não é recomendado como uma prática contínua, mas deve fazer você funcionar até que todos os seus testes sejam aprovados. Para obter mais detalhes, consulte a documentação infalível do maven .

Julie
fonte
2
Isso funcionou perfeitamente para mim, uma vez que entrei no plug-in maven-surefire e criei uma lista de inclusões que apontava para o pacote que eu queria executar.
Jherico de
2
A partir do JUnit 4.8.2, isso não funciona bem com testes parametrizados. Os métodos @BeforeClass da Suite serão executados após o método @ Parameterized.Parameters do teste, evitando qualquer dependência da configuração da Suite.
Anm
Em resposta a mim mesmo, ao usar @Theories, a chamada para o método @DataPoints é chamada após a @BeforeClass da Suite.
Anm
19
Desculpe pelo necro - mas adicionar BeforeClass / AfterClass a uma superclasse não funciona como esperado - eles ainda são chamados após a conclusão de cada classe de teste. Isso é para a posteridade.
Subu Sankara Subramanian
3
Esta ainda é uma abordagem válida? Como você consegue evitar a necessidade de enumerar a lista de classes de teste na anotação SuiteClasses?
Burhan Ali
34

Um colega meu sugeriu o seguinte: você pode usar um RunListener personalizado e implementar o método testRunFinished (): http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html#testRunFinished(org. junit.runner.Result)

Para registrar o RunListener, basta configurar o plugin surefire da seguinte forma: http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html seção "Usando ouvintes e repórteres personalizados"

Esta configuração também deve ser escolhida pelo plugin à prova de falhas. Esta solução é ótima porque você não precisa especificar Suites, classes de teste de pesquisa ou qualquer uma dessas coisas - ela permite que o Maven faça sua mágica, esperando que todos os testes terminem.

Martin Vysny
fonte
5
+1 Essa é a primeira solução utilizável que vi sem a manutenção incômoda de uma classe de suítes!
Stefan Haberl
6

Usando anotações, você pode fazer algo assim:

import org.junit.*;
import static org.junit.Assert.*;
import java.util.*;

class SomethingUnitTest {
    @BeforeClass
    public static void runBeforeClass()
    {

    }

    @AfterClass
    public static void runAfterClass()
    {  

    }

    @Before  
    public void setUp()
    {

    }

    @After
    public void tearDown()
    {

    }

    @Test
    public void testSomethingOrOther()
    {

    }

}
Jroc
fonte
4
A configuração e desmontagem precisam ser executados uma vez por execução. Isso só ajudaria se todos os testes estivessem em uma classe.
sblundy,
Isso apenas configura o conjunto de teste individual, não toda a etapa de teste
Ben
3

Aqui nós

  • atualizado para JUnit 4.5,
  • escreveu anotações para marcar cada classe de teste ou método que precisava de um serviço funcional,
  • escreveu manipuladores para cada anotação que continha métodos estáticos para implementar a configuração e desmontagem do serviço,
  • estendeu o Runner usual para localizar as anotações nos testes, adicionando os métodos do manipulador estático à cadeia de execução do teste nos pontos apropriados.

fonte
2

Quanto a "Nota: estamos usando o maven 2 para nossa construção. Eu tentei usar as fases de teste pré e pós-integração do maven, mas, se um teste falhar, o maven para e não executa o teste pós-integração , o que não ajuda em nada. "

você pode tentar o plug-in à prova de falhas, acho que ele tem a facilidade de garantir que a limpeza ocorra independentemente da configuração ou do status do estágio intermediário

Binod Pant
fonte
Sim, o plug-in à prova de falhas permitirá que você especifique configurações e desmontagens específicas. Embora eu não ache que a proteção contra falhas existisse no momento em que esta questão foi postada.
Jason Axelson
2

Desde que todos os seus testes possam estender uma aula "técnica" e estejam no mesmo pacote, você pode fazer um pequeno truque:

public class AbstractTest {
  private static int nbTests = listClassesIn(<package>).size();
  private static int curTest = 0;

  @BeforeClass
  public static void incCurTest() { curTest++; }

  @AfterClass
  public static void closeTestSuite() {
      if (curTest == nbTests) { /*cleaning*/ }             
  }
}

public class Test1 extends AbstractTest {
   @Test
   public void check() {}
}
public class Test2 extends AbstractTest {
   @Test
   public void check() {}
}

Esteja ciente de que esta solução tem muitas desvantagens:

  • deve executar todos os testes do pacote
  • deve subclassificar uma classe "techincal"
  • você não pode usar @BeforeClass e @AfterClass dentro de subclasses
  • se você executar apenas um teste no pacote, a limpeza não é feita
  • ...

Para obter informações: listClassesIn () => Como você encontra todas as subclasses de uma determinada classe em Java?

christophe blin
fonte
1
isso não é verdade, tanto quanto meus próprios testes mostram. Eu tenho uma superclasse que começa um glassfish embutido antes da aula e o desliga depois da aula. Tenho então 2 classes que se estendem dessa super classe. O beforeclass é executado antes de executar os testes definidos em cada classe.
Jonathan Morales Vélez
0

Pelo que eu sei, não há mecanismo para fazer isso no JUnit, no entanto, você pode tentar criar uma subclasse de Suite e substituir o método run () por uma versão que forneça ganchos.

user15299
fonte
0

Uma vez que o maven-surefire-plugin não executa a classe Suite primeiro, mas trata as classes suite e de teste da mesma forma, podemos configurar o plugin como abaixo para habilitar apenas as classes suite e desabilitar todos os testes. O Suite executará todos os testes.

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <includes>
                    <include>**/*Suite.java</include>
                </includes>
                <excludes>
                    <exclude>**/*Test.java</exclude>
                    <exclude>**/*Tests.java</exclude>
                </excludes>
            </configuration>
        </plugin>
Anurag Sharma
fonte
0

A única maneira de obter a funcionalidade que você deseja seria fazer algo como

import junit.framework.Test;  
import junit.framework.TestResult;  
import junit.framework.TestSuite;  

public class AllTests {  
    public static Test suite() {  
        TestSuite suite = new TestSuite("TestEverything");  
        //$JUnit-BEGIN$  
        suite.addTestSuite(TestOne.class);  
        suite.addTestSuite(TestTwo.class);  
        suite.addTestSuite(TestThree.class);  
        //$JUnit-END$  
     }  

     public static void main(String[] args)  
     {  
        AllTests test = new AllTests();  
        Test testCase = test.suite();  
        TestResult result = new TestResult();  
        setUp();  
        testCase.run(result);  
        tearDown();  
     }  
     public void setUp() {}  
     public void tearDown() {}  
} 

Eu uso algo assim no eclipse, então não tenho certeza de quão portátil é fora desse ambiente

Jroc
fonte
3
Este é um exemplo para JUnit3, e o OP pediu JUnit4, mas apenas no caso de alguns usuários JUnit3 acharem esta pergunta ... Para JUnit3, seria melhor se livrar do método main () e ter o método suite () agrupe o TestSuite em uma subclasse de junit.extensions.TestSetup. Você ainda tem as mesmas ressalvas que o exemplo de Julie sobre a execução de classes de teste individuais em seu IDE.
NamshubWriter
0

Se você não deseja criar um conjunto e precisa listar todas as suas classes de teste, pode usar a reflexão para encontrar o número de classes de teste dinamicamente e fazer uma contagem regressiva em uma classe base @AfterClass para fazer o tearDown apenas uma vez:

public class BaseTestClass
{
    private static int testClassToRun = 0;

    // Counting the classes to run so that we can do the tear down only once
    static {
        try {
            Field field = ClassLoader.class.getDeclaredField("classes");
            field.setAccessible(true);

            @SuppressWarnings({ "unchecked", "rawtypes" })
            Vector<Class> classes = (Vector<Class>) field.get(BlockJUnit4ClassRunner.class.getClassLoader());
            for (Class<?> clazz : classes) {
                if (clazz.getName().endsWith("Test")) {
                    testClassToRun++;
                }
            }
        } catch (Exception ignore) {
        }
    }

    // Setup that needs to be done only once
    static {
        // one time set up
    }

    @AfterClass
    public static void baseTearDown() throws Exception
    {
        if (--testClassToRun == 0) {
            // one time clean up
        }
    }
}

Se você preferir usar @BeforeClass em vez dos blocos estáticos, também pode usar um sinalizador booleano para fazer a contagem de reflexão e testar a configuração apenas uma vez na primeira chamada. Espero que isso ajude alguém, levei uma tarde para descobrir uma maneira melhor do que enumerar todas as classes em uma suíte.

Agora, tudo que você precisa fazer é estender essa classe para todas as suas classes de teste. Já tínhamos uma classe base para fornecer algumas coisas comuns para todos os nossos testes, então essa foi a melhor solução para nós.

A inspiração vem desta resposta SO https://stackoverflow.com/a/37488620/5930242

Se você não quiser estender essa classe a todos os lugares, esta última resposta do SO pode fazer o que você quiser.

FredBoutin
fonte