如何构建可配置的 JUnit4 测试套件?

2022-09-02 10:55:49

Guava 为用 JUnit3 编写的收集实现提供了一组广泛的测试,如下所示:

/*
 * Copyright (C) 2008 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
public class CollectionRemoveTester<E> extends AbstractTester<E> {

  @CollectionFeature.Require(SUPPORTS_REMOVE)
  @CollectionSize.Require(absent = ZERO)
  public void testRemove_present() {
     ...
  }
}

然后,通过使用传递给集合类型的一组功能和生成器的 s 来测试不同的集合,并且一个高度反射的框架标识要运行的测试方法集。TestSuiteBuilder

我想在JUnit4中构建类似的东西,但我不清楚如何去做:构建我自己的?理论?到目前为止,我最好的猜测是写一些类似的东西Runner

abstract class AbstractCollectionTest<E> {
   abstract Collection<E> create(E... elements);
   abstract Set<Feature> features();

   @Test
   public void removePresentValue() {
      Assume.assumeTrue(features().contains(SUPPORTS_REMOVE));
      ...
   }
}

@RunWith(JUnit4.class)
class MyListImplTest<E> extends AbstractCollectionTest<E> {
  // fill in abstract methods
}

一般的问题是这样的:在JUnit4中,我如何为接口类型构建一套测试,然后将这些测试应用于各个实现?


答案 1

在 Junit 中,您可以使用类别。例如,此套件将从注释为集成的 AllTestSuite 执行 al 测试:

import org.junit.experimental.categories.Categories;
import org.junit.experimental.categories.Categories.IncludeCategory;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Categories.class)
@IncludeCategory(Integration.class)
@Suite.SuiteClasses ({AllTestsSuite.class} )
public class IntegrationTestSuite {}

您还可以使用@ExcludeCategory。这对于消除缓慢的测试非常有用。类别类只是普通的旧 Java 类或接口。例如:

public interface Integration{}
public interface Performance{}
public interface Slow{}
public interface Database{}

您只需要以适当的方式注释您的测试:

@Category(Integration.class)
public class MyTest{

   @Test
   public void myTest__expectedResults(){
   [...]

一个测试可能有多个类别,如下所示:

   @Category({Integration.class,Database.class})  
   public class MyDAOTest{

为简单起见,我通常使用google工具箱创建一个套件,其中包含测试文件夹中的所有类:

import org.junit.runner.RunWith;

import com.googlecode.junittoolbox.ParallelSuite;
import com.googlecode.junittoolbox.SuiteClasses;

@RunWith(ParallelSuite.class)
@SuiteClasses({"**/**.class",           //All classes
             enter code here  "!**/**Suite.class" })    //Excepts suites
public class AllTestsSuite {}

这适用于在AllTestSuite中包含同一文件夹和子文件夹中的所有类,即使它们没有_Test sufix。但无法看到不在同一文件夹或子文件夹中的测试。junit-toolbox 在 Maven 中提供:

<dependency>
    <groupId>com.googlecode.junit-toolbox</groupId>
    <artifactId>junit-toolbox</artifactId>
    <version>2.2</version>
</dependency>

现在,您只需要执行适合您需求的套件:)

更新:在春季,有@IfProfileValue注释,允许您有条件地执行测试,例如:

@IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"})
@Test
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {

有关更多信息,请参见 Spring JUnit Testing Annotations


答案 2

关于是否建立自己的Runner...我认为你不应该立即尝试构建自己的测试,而是参数化你的单元测试。

一种选择是使用对类进行批注并插入一个带有批注的静态方法,该方法将用于使用 JUnit 测试的构造函数进行实际参数化。下面是一个我无耻地从 https://github.com/junit-team/junit/wiki/Parameterized-tests 中取出的例子:@RunWith(Parameterized.class)@Parameters

@RunWith(Parameterized.class)
public class FibonacciTest {
    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {     
                 { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }  
           });
    }

    private int fInput;

    private int fExpected;

    public FibonacciTest(int input, int expected) {
        fInput= input;
        fExpected= expected;
    }

    @Test
    public void test() {
        assertEquals(fExpected, Fibonacci.compute(fInput));
    }
}

这将使所有测试方法使用相同的参数,因为它们通常被分配给 JUnit 类中的相应字段。关键是这个静态方法中不同实现的实例化(Dagger,Guice,工厂,等等)。然后,它们将自动传递给构造函数,您将负责将它们分配给您将在测试方法中使用的字段。如您所见,与其使用示例的整数数组,不如将实现的实例放在其中。有关更多信息,请查看上面的链接。

第二个选项是将Zohhak与 https://github.com/piotrturski/zohhak 中的注释一起使用。这将允许您按方法而不是按类参数化单元测试。使用工厂实例化,这可能会更棘手,但是可以通过一些工作来非常优雅地制作。示例取自 Zohhak 网站:@RunWith(ZohhakRunner.class)

@TestWith({
    "clerk,      45'000 USD, GOLD",
    "supervisor, 60'000 GBP, PLATINUM"
})
public void canAcceptDebit(Employee employee, Money money, ClientType clientType) {
    assertTrue(   employee.canAcceptDebit(money, clientType)   );
}

我会从第一种方法开始,如果你击中了alimit,请转到第二种方法。干杯,祝你好运。


推荐