96

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

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

4

7 回答 7

104

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

还要检查这个例子

于 2008-12-11T10:32:28.833 回答
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());
    }

}
于 2008-12-11T11:04:15.337 回答
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 中生成。@TestFactorydynamicTest

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 中的输出

于 2017-03-27T20:18:48.667 回答
3

在 JUnit 3 中应该可以通过继承TestSuite和覆盖tests()列出文件的方法并为每个返回一个子类的实例,该实例TestCase将文件名作为构造函数参数并具有测试构造函数中给出的文件的测试方法。

在 JUnit 4 中它可能更容易。

于 2008-12-11T10:16:57.353 回答
2

您可以考虑使用JUnitParams library,因此您将有更多(更清洁)选项:

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

  • 更明确 - 参数在测试方法参数中,而不是类字段
  • 更少的代码 - 你不需要构造函数来设置参数
  • 您可以在一个类中混合使用参数化方法和非参数化方法
  • params 可以作为 CSV 字符串或从参数提供程序类传递
  • 参数提供者类可以有尽可能多的参数提供方法,以便您可以对不同的情况进行分组
  • 你可以有一个提供参数的测试方法(不再有外部类或静态)
  • 您可以在 IDE 中看到实际的参数值(在 JUnit 的参数化中,它只是连续的参数数量)
于 2015-05-20T21:09:28.990 回答
1

如果 TestNG 是一个选项,您可以将Parameters 与 DataProviders 一起使用。

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

此行为与 JUnit Theories不同,其中所有结果都集中在一个“理论”条目下,并且仅计为 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
===============================================
于 2014-07-17T02:44:10.937 回答
0

我遇到了类似的问题,最后编写了一个简单的 JUnit 4 运行程序,允许 med 动态生成测试。

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

于 2016-04-08T19:04:29.767 回答