it-swarm-pt.tech

Gancho de execução Antes e Depois do Suite no jUnit 4.x

Estou tentando executar a instalação e desmontagem para um conjunto de testes de integração, usando o jUnit 4.4 para executar os testes. A desmontagem precisa ser executada de maneira confiável. Estou tendo outros problemas com o TestNG, por isso estou procurando a porta de volta para o jUnit. Quais ganchos estão disponíveis para execução antes da execução de qualquer teste e após a conclusão de todos os testes?

Nota: estamos usando o maven 2 para nossa compilação. Tentei usar as fases pre- & post-integration-test do maven, mas, se um teste falhar, o maven para e não executa post-integration-test, o que não ajuda.

81
sblundy

Sim, é possível executar com segurança métodos de configuração e desmontagem antes e depois de qualquer teste em um conjunto de testes. Deixe-me demonstrar no 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 classe Test1 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 o Test2 é semelhante. Se você executasse o TestSuite, obteria:

setting up
test1
test2
tearing down

Assim, você pode ver que a configuração/desmontagem é executada apenas 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 plugin maven surefire gosta de executar testes individualmente e não faz parte de um conjunto. Nesse caso, eu recomendaria criar uma superclasse que cada classe de teste estende. A superclasse contém os métodos anotados @BeforeClass e @AfterClass. Embora não seja tão limpo quanto o método acima, acho que funcionará para você.

Quanto ao problema com testes com falha, é possível configurar maven.test.error.ignore para que a construção continue nos testes com falha. Isso não é recomendado como uma prática contínua, mas deve mantê-lo funcionando até que todos os seus testes passem. Para mais detalhes, consulte a documentação do maven surefire .

111
Julie

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 maneira: http://maven.Apache.org/surefire/maven-surefire-plugin/examples/junit.html seção "Usando ouvintes e repórteres personalizados"

Essa configuração também deve ser escolhida pelo plug-in à prova de falhas. Essa solução é ótima porque você não precisa especificar Suítes, classes de teste de pesquisa ou nada disso - permite que o Maven faça sua mágica, aguardando a conclusão de todos os testes.

32
Martin Vysny
11
Tobogganski

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()
    {

    }

}
7
Jroc

Aqui nós

  • atualizado para o JUnit 4.5,
  • escreveu anotações para marcar cada classe ou método de teste que precisava de um serviço em funcionamento,
  • 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.
3
carym

Desde que todos os seus testes possam estender uma classe "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 das subclasses
  • se você executar apenas um teste no pacote, a limpeza não será feita
  • ...

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

2
christophe blin

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

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

2
Binod Pant

Se você não deseja criar um conjunto e precisar listar todas as suas classes de teste, use a reflexão para encontrar o número de classes de teste dinamicamente e faça a 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 poderá usar um sinalizador booleano para fazer a contagem de reflexões 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, portanto 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 deseja estender essa classe para todos os lugares, esta última SO resposta pode fazer o que você deseja.

0
FredBoutin

Como o maven-surefire-plugin não executa a classe Suite primeiro, mas trata as suítes e as classes de teste da mesma forma, podemos configurar o plug-in como abaixo para ativar apenas as classes da suíte e desativar 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>
0
Anurag Sharma

A única maneira de pensar, em seguida, para obter a funcionalidade desejada 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

0
Jroc

Até onde eu sei, não há mecanismo para fazer isso no JUnit, no entanto, você pode tentar subclassificar o Suite e substituir o método run () por uma versão que fornece ganchos.

0
user15299