Teste JUnit com número dinâmico de testes

95

Em nosso projeto, tenho vários testes JUnit que, por exemplo, pegam cada arquivo de um diretório e executam um teste nele. Se eu implementar um testEveryFileInDirectorymétodo no, TestCaseisso aparecerá como apenas um teste que pode falhar ou ter sucesso. Mas estou interessado nos resultados de cada arquivo individual. Como posso escrever um TestCase/ de TestSuitemodo que cada arquivo apareça como um teste separado, por exemplo, no TestRunner gráfico do Eclipse? (Codificar um método de teste explícito para cada arquivo não é uma opção).

Compare também a questão ParameterizedTest com um nome no Eclipse Testrunner .

Hans-Peter Störr
fonte
1
Consulte também stackoverflow.com/questions/3257080/…
Vadzim

Respostas:

102

Dê uma olhada nos testes parametrizados no JUnit 4.

Na verdade, eu fiz isso há alguns dias. Vou tentar explicar ...

Primeiro construa sua classe de teste normalmente, já que você estava apenas testando com um arquivo de entrada. Decore sua aula com:

@RunWith(Parameterized.class)

Construir um construtor que recebe a entrada que mudará a cada chamada de teste (neste caso, pode ser o próprio arquivo)

Então, construa um método estático que retornará um Collectionde arrays. Cada array na coleção conterá os argumentos de entrada para seu construtor de classe, por exemplo, o arquivo. Decore este método com:

@Parameters

Aqui está um exemplo de aula.

@RunWith(Parameterized.class)
public class ParameterizedTest {

    private File file;

    public ParameterizedTest(File file) {
        this.file = file;
    }

    @Test
    public void test1() throws Exception {  }

    @Test
    public void test2() throws Exception {  }

    @Parameters
    public static Collection<Object[]> data() {
        // load the files as you want
        Object[] fileArg1 = new Object[] { new File("path1") };
        Object[] fileArg2 = new Object[] { new File("path2") };

        Collection<Object[]> data = new ArrayList<Object[]>();
        data.add(fileArg1);
        data.add(fileArg2);
        return data;
    }
}

Verifique também este exemplo

bruno conde
fonte
1
Obrigado! O método JUnit 4 é melhor do que o método JUnit 3 dado em outra resposta, uma vez que o JUnit 3 confunde o executor de teste do eclipse e com o método JUnit 4 você pode reexecutar os testes etc. nome para o teste - mostra apenas [0], [1] etc.
Hans-Peter Störr
@hstoerr, Parece que isso será no próximo lançamento do JUnit :-) github.com/KentBeck/junit/commit/…
rescdsk
Como você transformaria isso se quisesse que cada execução [com uma combinação de dados diferente] modificasse o nome dessa execução de teste? [Ou seja, o arquivo Path1 seria testado como: test1Path1, test2Path?
monksy
^ Link atualizado: github.com/junit-team/junit4/commit/…
Alexander Udalov
27

JUnit 3

public class XTest extends TestCase {

    public File file;

    public XTest(File file) {
        super(file.toString());
        this.file = file;
    }

    public void testX() {
        fail("Failed: " + file);
    }

}

public class XTestSuite extends TestSuite {

    public static Test suite() {
        TestSuite suite = new TestSuite("XTestSuite");
        File[] files = new File(".").listFiles();
        for (File file : files) {
            suite.addTest(new XTest(file));
        }
        return suite;
    }

}

JUnit 4

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class TestY {

    @Parameters
    public static Collection<Object[]> getFiles() {
        Collection<Object[]> params = new ArrayList<Object[]>();
        for (File f : new File(".").listFiles()) {
            Object[] arr = new Object[] { f };
            params.add(arr);
        }
        return params;
    }

    private File file;

    public TestY(File file) {
        this.file = file;
    }

    @Test
    public void testY() {
        fail(file.toString());
    }

}
McDowell
fonte
11

Testes Parametrizados Junit 5

Os testes parametrizados JUnit 5 suportam isso, permitindo o uso de um método como fonte de dados :

@ParameterizedTest
@MethodSource("fileProvider")
void testFile(File f) {
    // Your test comes here
}

static Stream<File> fileProvider() {
    return Arrays.asList(new File(".").list()).stream();
}

JUnit 5 DynamicTests

O JUnit 5 também suporta isso por meio da noção de a DynamicTest, que deve ser gerado em a @TestFactory, por meio do método estático dynamicTest.

import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

import java.util.stream.Stream;

@TestFactory
public Stream<DynamicTest> testFiles() {
    return Arrays.asList(new File(".").list())
            .stream()
            .map((file) -> dynamicTest(
                    "Test for file: " + file,
                    () -> { /* Your test comes here */ }));
}

Os testes executados em seu IDE (IntelliJ aqui) serão exibidos assim:

Saída no IntelliJ

avandeursen
fonte
3

Deve ser possível no JUnit 3 herdando TestSuitee substituindo o tests()método para listar os arquivos e para cada retorno uma instância de uma subclasse deTestCase que leva o nome do arquivo como parâmetro do construtor e tem um método de teste que testa o arquivo fornecido no construtor.

No JUnit 4 pode ser ainda mais fácil.

Michael Borgwardt
fonte
2

Você poderia considerar o uso da biblioteca JUnitParams , então você teria mais algumas opções (mais limpas):

@org.junit.runner.RunWith(junitparams.JUnitParamsRunner.class)
public class ParameterizedTest {

    @org.junit.Test
    @junitparams.Parameters(method = "data")
    public void test1(File file) throws Exception {  }

    @org.junit.Test
    @junitparams.Parameters(method = "data")
    public void test2(File file) throws Exception {  }

    public static File[] data() {
        return new File[] { new File("path1"), new File("path2") };
    }
}

@org.junit.runner.RunWith(junitparams.JUnitParamsRunner.class)
public class ParameterizedTest {

    @org.junit.Test
    @junitparams.Parameters(value = { "path1", "path2" })
    public void test1(String path) throws Exception {
        File file = new File(path);
    }

    @org.junit.Test
    @junitparams.Parameters(value = { "path1", "path2" })
    public void test2(String path) throws Exception {
        File file = new File(path);
    }
}

Você pode ver mais exemplos de uso aqui .

Além do JUnitParams, por que escrever testes parametrizados com ele é mais fácil e legível :

O projeto JUnitParams adiciona um novo executor ao JUnit e fornece testes parametrizados muito mais fáceis e legíveis para JUnit> = 4.6.

Principais diferenças para o executor parametrizado JUnit padrão:

  • mais explícito - os parâmetros estão em parâmetros de método de teste, não em campos de classe
  • menos código - você não precisa de um construtor para configurar os parâmetros
  • você pode misturar métodos parametrizados com não parametrizados em uma classe
  • params podem ser passados ​​como uma string CSV ou de uma classe de provedor de parâmetros
  • a classe do provedor de parâmetros pode ter quantos parâmetros de fornecimento de métodos você quiser, para que você possa agrupar diferentes casos
  • você pode ter um método de teste que fornece parâmetros (sem classes externas ou estáticas mais)
  • você pode ver os valores reais dos parâmetros em seu IDE (no Parametrizado do JUnit são apenas números consecutivos de parâmetros)
falsarella
fonte
1

Se TestNG for uma opção, você pode usar Parâmetros com DataProviders .

O teste de cada arquivo individual terá seu resultado mostrado no relatório baseado em texto ou na IU do plugin TestNG do Eclipse. O número total de testes executados contará cada um de seus arquivos individualmente.

Esse comportamento difere das teorias JUnit , nas quais todos os resultados são agrupados em uma entrada de "teoria" e contam como apenas 1 teste. Se você quiser relatórios de resultados separados em JUnit, você pode tentar testes parametrizados .

Teste e entradas

public class FileTest {

    @DataProvider(name="files")
    public File[][] getFiles(){
        return new File[][] {
            { new File("file1") },
            { new File("file2") }
        };
        // or scan a directory
    }

    @Test(dataProvider="files")
    public void testFile(File file){
        //run tests on file
    }
}

Saída de exemplo

PASSED: testFile(file1)
PASSED: testFile(file2)

===============================================
    Default test
    Tests run: 2, Failures: 0, Skips: 0
===============================================
Ben Hutchison
fonte
Não sei sobre teorias, mas os testes parametrizados no JUnit são mostrados separadamente no eclipse, não agrupados.
Hans-Peter Störr