JUnit test с динамическим количеством тестов

95

В нашем проекте у меня есть несколько тестов JUnit, которые, например, берут каждый файл из каталога и запускают на нем тест. Если я реализую testEveryFileInDirectoryметод в, TestCaseэто будет отображаться как только один тест, который может быть неудачным или успешным. Но меня интересуют результаты по каждому отдельному файлу. Как я могу написать TestCase/ так TestSuite, чтобы каждый файл отображался как отдельный тест, например, в графическом TestRunner Eclipse? (Кодирование явного метода тестирования для каждого файла не является вариантом.)

Сравните также вопрос ParameterizedTest с именем в Eclipse Testrunner .

Ханс-Петер Штёрр
источник
1
См. Также stackoverflow.com/questions/3257080/…
Vadzim

Ответы:

102

Взгляните на параметризованные тесты в JUnit 4.

На самом деле я сделал это несколько дней назад. Попробую объяснить ...

Сначала создайте свой тестовый класс обычным образом, так как вы просто тестировали с одним входным файлом. Украсьте свой класс:

@RunWith(Parameterized.class)

Создайте один конструктор, который принимает ввод, который будет изменяться при каждом тестовом вызове (в этом случае это может быть сам файл)

Затем создайте статический метод, который вернет Collectionмассив массивов. Каждый массив в коллекции будет содержать входные аргументы для вашего конструктора класса, например, файл. Украсьте этот метод:

@Parameters

Вот образец класса.

@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;
    }
}

Также проверьте этот пример

Бруно Конде
источник
1
Спасибо! Метод JUnit 4 лучше, чем метод JUnit 3, указанный в другом ответе, поскольку JUnit 3 сбивает с толку средство запуска тестов eclipse, а с помощью метода JUnit 4 вы можете повторно выполнить тесты и т. Д. Мне только интересно, как я могу показать затмение название теста - он показывает только [0], [1] и т. д.
Hans-Peter Störr
@hstoerr, Похоже, это будет в следующем выпуске JUnit :-) github.com/KentBeck/junit/commit/…
rescdsk
Как бы вы это изменили, если бы вы хотели, чтобы каждый запуск [с другой комбинацией данных] изменял имя этого тестового прогона? [Т.е. файл Path1 будет тестироваться как: test1Path1, test2Path?
monksy
^ Обновленная ссылка: github.com/junit-team/junit4/commit/…
Александр Удалов
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());
    }

}
Макдауэлл
источник
11

Параметризованные тесты Junit 5

Параметризованные тесты JUnit 5 поддерживают это, позволяя использовать метод в качестве источника данных :

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

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

Динамические тесты JUnit 5

JUnit 5 также поддерживает это через понятие a DynamicTest, которое должно быть сгенерировано в a @TestFactoryс помощью статического метода 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 */ }));
}

Тесты, запущенные в вашей IDE (здесь IntelliJ), будут отображаться следующим образом:

Вывод в IntelliJ

Avandeursen
источник
3

Должно быть возможно в JUnit 3 путем наследования TestSuiteи переопределения tests()метода для перечисления файлов и для каждого возврата экземпляра подкласса, TestCaseкоторый принимает имя файла в качестве параметра конструктора и имеет тестовый метод, который проверяет файл, указанный в конструкторе.

В JUnit 4 это могло быть еще проще.

Майкл Боргвардт
источник
2

Вы можете рассмотреть возможность использования библиотеки JUnitParams , чтобы у вас было еще несколько (более чистых) вариантов:

@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);
    }
}

Вы можете увидеть больше примеров использования здесь .

В дополнение к JUnitParams, почему писать параметризованные тесты с его помощью проще и удобнее :

Проект JUnitParams добавляет новый исполнитель в JUnit и предоставляет намного более простые и удобочитаемые параметризованные тесты для JUnit> = 4.6.

Основные отличия от стандартного JUnit Parametrised runner:

  • более явный - параметры находятся в параметрах метода тестирования, а не в полях класса
  • меньше кода - вам не нужен конструктор для настройки параметров
  • вы можете смешивать параметризованные и непараметризированные методы в одном классе
  • params можно передать как строку CSV или из класса поставщика параметров
  • класс поставщика параметров может иметь столько параметров, предоставляющих методы, сколько вы хотите, чтобы вы могли группировать разные случаи
  • у вас может быть тестовый метод, который предоставляет параметры (больше никаких внешних классов или статики)
  • вы можете видеть фактические значения параметров в своей среде IDE (в JUnit Parametrised это только последовательные числа параметров)
фальсарелла
источник
1

Если TestNG является вариантом, вы можете использовать параметры с DataProviders .

Результат каждого теста отдельного файла будет отображаться в текстовом отчете или в пользовательском интерфейсе плагина Eclipse TestNG. При общем количестве выполненных тестов будет учитываться каждый из ваших файлов индивидуально.

Это поведение отличается от JUnit Theories , в котором все результаты объединяются в одну «теоретическую» запись и считаются только за 1 тест. Если вам нужен отдельный отчет о результатах в JUnit, вы можете попробовать параметризованные тесты .

Тест и входы

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
    }
}

Пример вывода

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

===============================================
    Default test
    Tests run: 2, Failures: 0, Skips: 0
===============================================
Бен Хатчисон
источник
Я не знаю о теориях, но параметризованные тесты в JUnit показаны отдельно в eclipse, а не вместе.
Hans-Peter Störr
0

У меня была аналогичная проблема, и в итоге я написал простой бегун JUnit 4, который позволяет med динамически генерировать тесты.

https://github.com/kimble/junit-test-factory

Kimble
источник