具有动态测试数量的JUnit测试


95

在我们的项目中,我有几个JUnit测试,例如从目录中获取每个文件并对其进行测试。如果我在中实现了一种testEveryFileInDirectory方法,则TestCase显示为仅一个可能失败或成功的测试。但是我对每个文件的结果都很感兴趣。如何编写一个TestCase/ TestSuite,使每个文件显示为单独的测试,例如在Eclipse的图形TestRunner中?(为每个文件编写显式测试方法不是一种选择。)

还将问题ParameterizedTest与Eclipse Testrunner中的名称进行比较


Answers:


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 Method可以重新执行测试等。我只是想知道如何让Eclipse显示一个命名为测试-只显示[0],[1]等
汉斯-彼得·斯托

@hstoerr,像这看起来将是JUnit的:-)的下一个版本github.com/KentBeck/junit/commit/...
rescdsk

如果您希望每个运行(具有不同的数据组合)来修改测试运行的名称,您将如何转换呢?[即Path1文件将被测试为:test1Path1,test2Path?
僧侣2012年


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@TestFactory通过static方法在a中生成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输出


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参数化运行器的主要区别:

  • 更明确-参数在测试方法参数中,而不是类字段中
  • 更少的代码-您不需要构造函数来设置参数
  • 您可以在一类中将参数化方法与非参数化方法混合使用
  • 参数可以作为CSV字符串或从参数提供程序类传递
  • 参数提供程序类可以具有任意数量的参数提供方法,以便可以将不同的情况分组
  • 您可以使用提供参数的测试方法(不再使用外部类或静态变量)
  • 您可以在IDE中看到实际的参数值(在JUnit的Parametricsed中,它只是连续的参数数量)

1

如果可以选择TestNG,则可以将Parameters与DataProviders一起使用。

每个文件的测试结果都将显示在基于文本的报告或Eclipse的TestNG插件UI中。运行的总测试次数将分别计算每个文件。

此行为不同于JUnit 理论,在JUnit 理论中,所有结果都集中在一个“ theory”条目下,并且仅计为1个测试。如果要在JUnit中单独报告结果,可以尝试Parameterized Tests

测试和输入

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中单独显示,而不是集中在一起。
汉斯·彼得·斯特尔2014年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.