参数化方法运行两次

yus*_*sif 7 java junit unit-testing parameterized maven

我正在运行这段代码,并意识到该getAllParameters()方法由于某种原因运行了两次。由于静态字段enumMap是在该方法之外初始化的,因此它会被填充两次,这会导致重复元素并使我正在运行的测试失败。

我认为enumMap在方法内部初始化可以解决问题,因为当该方法第二次运行时地图确实会重置。

尽管这解决了问题,但我想知道为什么在运行 Maven Test 时会发生这种情况?我尝试了参数的数量,认为这可能会影响方法运行的次数,但它似乎只运行了两次。

@RunWith(Parameterized.class)
public class MyTest {

    private static Map<String, List<Class<? extends LocalizedJsonEnum>>> enumMap = new HashMap<>();

    @Parameter
    @SuppressWarnings({"WeakerAccess", "unused"})
    public Class<? extends LocalizedJsonEnum> currentEnum;
    @Parameter(value = 1)
    @SuppressWarnings({"WeakerAccess", "unused"})
    public  String currentClassName;

    /**
     * Generate a list of all the errors to run our test against.
     *
     * @return the list
     */
    @Parameters(name = "{1}.class")
    public static Collection<Object[]> getAllParameters() throws Exception {
        Collection<Object[]> parameters = new LinkedList<>();
        Reflections reflections = new Reflections("com.class.path");
        Set<Class<? extends LocalizedJsonEnum>> JsonEnums = reflections.getSubTypesOf(LocalizedJsonEnum.class);

        //workaround: initialize here
        //enumMap = new HashMap<>();

        //some code that inserts elements into the enumMap and parameters 
        return parameters;
    }


@Test
public void testEnumIdentifierIsNotDuplicated() throws Exception {

    String enumId;
    if (currentEnum.isAnnotationPresent(Identifier.class)) {
        enumId = currentEnum.getAnnotation(Identifier.class).value();
    } else {
        enumId = currentEnum.getSimpleName();
    }
    List<Class<? extends LocalizedJsonEnum>> enumList = enumMap.get(enumId);

    if (enumList.size() > 1) {

        StringBuilder sb = new StringBuilder("Enum or identifier [" + enumId + "] has been duplicated in the following classes:\n");
        for (int listIndex = 0; listIndex < enumList.size(); listIndex++) {
            Class<? extends LocalizedJsonEnum> enumDuplicate = enumList.get(listIndex);
            sb.append("   [").append(listIndex).append("] Enum Class:[").append(enumDuplicate.getName()).append("]\n");
        }
        fail(sb.toString());
    }
}
Run Code Online (Sandbox Code Playgroud)

rzw*_*oot 1

您已经有了一个参数化的测试函数,因此,系统将运行它两次 - 每个参数一次。

关于如何做到这一点,有不同的合理方法。

一个简单的解决方案就是做最明显的事情。从测试框架来看:

  1. 创建once的实例MyTest
  2. 对第一个参数调用一次测试方法。
  3. 再次调用它,获取第二个参数。

当然,这会导致字段和调用的“重用”——第二次测试运行中的对象被“污染”。因此,这实际上并不是任何测试框架的工作原理。相反,他们这样做:

  1. 创建 的实例MyTest
  2. 对第一个参数调用一次测试方法。
  3. 扔掉以前的实例并创建一个新实例,尽可能从头开始。
  4. 使用新实例再次调用第二个参数。

但是,因为你正在使用static这个仍然不够好。每个测试调用的一个新实例不会执行任何“重置”静态内容的操作。因此,测试框架可以执行这种更加复杂的设置:

  1. 创建自定义类加载器实例。
  2. 用它来加载测试类。
  3. 创建一个新实例。
  4. 使用第一个参数运行第一个测试。
  5. 扔掉实例和整个类加载器。
  6. 回到第 1 项开始进行第二次运行。

这将执行您想要的操作:每个“测试调用”(这里有 1 个测试方法和 2 个调用;每个参数选项一个)都会获得一张完全空白的石板。

然而,这将对性能产生很大影响。类加载比较昂贵;你需要在磁盘上打开一个文件,即使你依赖操作系统来缓存(因为每个测试每次都会打开 jar 文件的同一部分或诸如此类的东西),将一袋字节转换为类定义,运行验证器,这并不便宜。“运行测试套件需要很长时间”的特性是一种生产力消耗,没有人愿意为此付出代价,但是,拥有“干净的石板”测试,其中测试运行的顺序对结果没有影响也是每个人都想要的。那么我们在哪里切这个特殊的蛋糕呢?我们是否为人们提供了他们喜欢的“测试独立性”,但代价是测试套件运行缓慢?或者我们是否让他们尽可能快地进行测试运行,但像昨天的报纸一样抛弃测试独立性的概念?

真正的答案是在中间的某个地方,并带有一些可配置性。

这是我强烈建议你做的:

  1. static除非您有意希望在多次调用之间重用测试的某些方面,否则请避免在测试代码中使用。
  2. 如果您明确想要执行一次任务,然后将其“重用”多次以进行多次测试调用,请使用以下工具:、 、 和@Setup应该@Teardown用于@BeforeEach此内容。@BeforeAll@BeforeClass

现在您可以完全控制遇到的问题:

您可以将“创建” 的代码放在enumList一个单独的方法中,该方法本身不是@Test. 事实并非如此static

您将此方法标记为好像@BeforeAll您希望它运行一次。@BeforeEach如果您更喜欢较慢但更“独立”的概念,则可以标记它。当然,代码应该“重置”(它不应该只是添加内容,它应该清除/创建一个新列表并填充它)。