24

我有一门课,我用它作为单元测试的基础。在这个类中,我为我的测试初始化​​整个环境,设置数据库映射,在多个表中输入许多数据库记录,等等。该类有一个带有 @BeforeClass 注释的方法来进行初始化。接下来,我使用具有@Test 方法的特定类扩展该类。

我的问题是,由于所有这些测试类的前级完全相同,我如何确保它们对所有测试只运行一次。 一个简单的解决方案是我可以将所有测试放在一个类中。但是,测试的数量很大,而且它们是根据功能头进行分类的。因此它们位于不同的类中。但是,由于它们需要完全相同的设置,因此它们继承了 @BeforeClass。因此,每个测试类至少完成一次整个设置,总共花费的时间比我希望的要多得多。

不过,我可以将它们全部放在一个包下的各种子包中,因此,如果有办法,我如何为该包中的所有测试运行一次设置,那就太好了。

4

6 回答 6

20

使用 JUnit4 测试套件,您可以执行以下操作:

@RunWith(Suite.class)
@Suite.SuiteClasses({ Test1IT.class, Test2IT.class })
public class IntegrationTestSuite
{
    @BeforeClass
    public static void setUp()
    {
        System.out.println("Runs before all tests in the annotation above.");
    }

    @AfterClass
    public static void tearDown()
    {
        System.out.println("Runs after all tests in the annotation above.");
    }
}

然后你像运行一个普通的测试类一样运行这个类,它会运行你所有的测试。

于 2016-10-25T19:00:47.063 回答
8

JUnit 不支持这一点,您将不得不对单例使用标准的 Java 解决方法:将通用设置代码移动到静态代码块中,然后在此类中调用一个空方法:

 static {
     ...init code here...
 }

 public static void init() {} // Empty method to trigger the execution of the block above

确保所有测试都调用init(),例如我将其放入@BeforeClass方法中。或者将静态代码块放入共享基类中。

或者,使用全局变量:

 private static boolean initialize = true;
 public static void init() {
     if(!initialize) return;
     initialize = false;

     ...init code here...
 }
于 2013-09-02T13:40:18.113 回答
3

Create one base class for all tests:

public class BaseTest {
    static{
        /*** init code here ***/
    }   
}

and every test should inherit from it:

public class SomeTest extends BaseTest {

}
于 2015-01-20T14:09:19.713 回答
1

您可以BaseTest使用一种方法创建一个类@BeforeClass,然后让所有其他测试从它继承。这样,当每个测试对象被构造时,@BeforeClass就会被执行。

还要避免对所有测试套件只执行一次,因为所有测试用例都应该是独立的。@BeforeClass每个测试用例应该只执行一次,而不是测试套件。

于 2018-02-13T11:26:01.330 回答
0

不确定是否有人仍在使用 JUnit 并尝试在不使用 Spring Runner 的情况下修复它(也就是没有 spring 集成)。TestNG 有这个功能。但这是一个基于 JUnit 的解决方案。

像这样为每个线程操作创建一个 RunOnce。这维护了操作已运行的类的列表。

public class RunOnceOperation {
private static final ThreadLocal t = new ThreadLocal();

public void run(Function f) {
    if (t.get() == null) {
        t.set(Arrays.asList(getClass()));
        f.apply(0);
    } else {
        if (!((List) t.get()).contains(getClass())) {
            ((List) t.get()).add(getClass());
            f.apply(0);
        }
    }
  }
}

回到你的单元测试

@Before
public beforeTest() {
    operation.run(new Function<Integer, Void>() {
        @Override
        public Void apply(Integer t) {
            checkBeanProperties();
            return null;
        }
    });
}

private void checkBeanProperties() {
   //I only want to check this once per class.
   //Also my bean check needs instance of the class and can't be static.
}


My function interface is like this:

interface Function<I,O> {
 O apply(I i); 
}

当您使用这种方式时,您可以使用 ThreadLocal 对每个类执行一次操作。

于 2014-03-05T01:20:36.780 回答
0

如果您可以容忍将 spring-test 添加到您的项目,或者您已经在使用它,那么一个好的方法是使用这里描述的技术:How to load DBUnit test data once per case with Spring Test

于 2013-09-02T22:20:04.880 回答