在套件中使用@BeforeClass @Before 编写 JUnit 测试

Writing JUnit tests with @BeforeClass @Before in Suites

我正在使用 JUnit 4 测试带有内存数据库的后端系统。我正在使用 @BeforeClass @Before @After@AfterClass.

到目前为止,它在 class 水平上运行良好。

@BeforeClass 包含数据库设置,它很慢但每个测试会话只需要完成一次。

@Before 只是为 运行 的下一次测试擦干净了石板。挺快的。

我的测试看起来像这样:

class CompanyTest  {

    @BeforeClass
    public static void beforeAll() throws Exception {
        BeforeAll.run(); //Setup In Memory Database!!! Very time intensive!!!
    }
    @Before
    public void beforeTest() throws Exception {
        BeforeTest.run(); //Setup data in database. Relatively quick
    }


    @Test
    public void testCreateCompany() throws Exception {
        ///...
    }
    @Test
    public void testDeleteCompany() throws Exception {
        ///...
    }
    @Test
    public void testAdminCompany() throws Exception {
        ///...
    }


    @After
    public void afterTest() {
        AfterTest.run(); //clear data
    }
    @AfterClass
    public static void afterAll() throws Exception {
        AfterAll.run(); //tear down database
    }
}

到目前为止,它在 class 水平上运行良好。

我可以(在 Eclipse 中)右键单击单个测试,它将 运行 @BeforeClass 然后 @Before.

我也可以(在 Eclipse 中)单击 class 本身,它只会 运行 @BeforeClass 一次然后 @Before 每次测试前。

.....但这个原则如何扩展到套件级别?

我想 运行 @BeforeClass 在我的套件中的所有 classes 之前。如果我这样写我的套件:

@Suite.SuiteClasses({ CompanyTest.class, CustomerTest.class, SomeOtherTest.class, })

public class AllTests {

    @BeforeClass
    public static void beforeAll() throws Exception {
        BeforeAll.run();
    }

    @AfterClass
    public static void afterAll() throws Exception {
        AfterAll.run();
    }
}

...我需要从所有测试 classes 中删除 @BeforeClass。这很烦人,因为我有很多测试 classes,我不想删除我的 @BeforeClass 因为我想单独测试它们。

我的基本意思是:

是否有简单的方法(在 IDE 中单击鼠标)在 (a) 方法级别、(b) class 级别和(c) 套件级别,同时保持会话级别的设置和拆卸过程?

所以我有一个类似的问题(数据库初始化、连接池、缓存数据等)需要在 运行 任何和所有测试之前完成一次。这通常在应用程序服务器启动时完成。对于需要这个的测试,我添加了一个基础 class:

public class BaseInitializedTest {
    private boolean isInitialized;

    @BeforeClass
    public static void init() {
        doOnetimeInit();

        // Do other per unique class initialization here
    }

    private static synchronized void doOnetimeInit() {
        if (!isInitialized) {
            // Do you one time init here

            isInitialized = true;
        }
    }

    @AfterClass
    public static void deinit() {
        // Cleanup
    }
}

然后我的测试需要初始化:

public class SomethingTest extends BaseInitializedTest {
    @Test
    public testSomething() {
        // Your test here
    }
}

并非所有测试都需要初始化堆栈,但那些需要初始化的测试将从该基础继承 class 并且它将正确处理初始化。希望对您有所帮助。

您需要的是通过不同的入口点管理全局状态。在您的 BeforeAll 或 AfterAll class 中,您可以保留一个静态引用,即一个 AtomicBoolean 来跟踪您是否已经执行过它。

现在的问题 - beforeAll 和 afterAll 有两个单独的 classes - class 应该负责那个标志。

因此我建议,您定义一个 Rule,其中包含所有用于设置和拆卸的逻辑(基本上是 BeforeAll 和 AfterAll 的所有代码)并管理用于跟踪初始化的全局状态。

基本上是这样的

class MyRule implements TestRule {
    static final AtomicBoolean INITIALIZED = new AtomicBoolean();

    @Override
    public Statement apply(final Statement base, final Description description) {

        return new Statement() {

            @Override
            public void evaluate() throws Throwable {
                boolean needsTearDown = false;
                try {
                    if (INITIALIZED.compareAndSet(false, true)) {
                        BeforeAll.run();
                        needsTearDown = true;
                    }
                    base.evaluate();
                } finally {
                    if (needsTearDown) {
                        AfterAll.run();
                        INITIALIZED.set(false);
                    }
                }
            }
        };
    }
}

然后在您的测试和套件中添加

class YourTestOrSuite {

  @ClassRule
  public static MyRule rule = new MyRule();
}