使用Theories时如何在jUnit中获得条件执行?

时间:2014-02-24 20:22:46

标签: java junit

我有一个使用Theories运行器测试接口的抽象测试用例。接口的每个实现都具有测试用例的具体实现,其中一个实现使用Postgres。我想只在与Postgres数据库的连接实际可用时才运行该测试用例,否则将被忽略。

我无法使用Assume,因为如果理论的所有数据点都无法通过假设,Theories测试运行器将会失败。

我正在测试一个ResourceStore,它本质上是一个简单的文件系统。它接受路径并返回Resource对象,这些对象可能由文件系统支持,或者由Postgres之类的其他东西支持。我正在使用Theories来测试返回的资源是否遵循某些规则,以便实现彼此一致。

基类看起来像这样(导入和修剪的大多数特定测试)

/**
 * JUnit Theory test class for Resource invariants. Subclasses should provide representative 
 * DataPoints to test.
 * 
 */
@RunWith(Theories.class)
public abstract class ResourceTheoryTest {

    @Rule
    public ExpectedException exception = ExpectedException.none();

    protected abstract Resource getResource(String path) throws Exception;

    @Theory
    public void theoryNotNull(String path) throws Exception {
        Resource res = getResource(path);

        assertThat(res, notNullValue());
    }

    @Theory
    public void theoryExtantHaveDate(String path) throws Exception {
        Resource res = getResource(path);

        assumeThat(res, defined());

        long result = res.lastmodified();

        assertThat(result, notNullValue());
    }

}

基于文件系统的实现创建了一个临时目录,用一些文件设置它,然后点击正在测试的存储路径,一些是现有的,一些不是。

public class FileSystemResourceTheoryTest extends ResourceTheoryTest {

    FileSystemResourceStore store;

    @Rule
    public TemporaryFolder folder= new TemporaryFolder();


    @DataPoints
    public static String[] testPaths() {
        return new String[]{"FileA","FileB", "DirC", "DirC/FileD", "DirE", "UndefF", "DirC/UndefF", "DirE/UndefF", "DirE/UndefG/UndefH/UndefI"};
    }

    @Override
    protected Resource getResource(String path) throws Exception{
        return store.get(path);
    }

    @Before
    public void setUp() throws Exception {
        folder.newFile("FileA");
        folder.newFile("FileB");
        File c = folder.newFolder("DirC");        
        (new File(c, "FileD")).createNewFile();
        folder.newFolder("DirE");
        store = new FileSystemResourceStore(folder.getRoot());
    }

}

基于JDBC的模块有另一个基于第一个的抽象测试用例,它使用TestSupport委托来抽象连接和设置测试环境的方言差异。同样的支持类也用于其他非理论测试。

public abstract class AbstractJDBCResourceTheoryTest extends ResourceTheoryTest {

    DatabaseTestSupport support;

    @DataPoints
    public static String[] testPaths() {
        return new String[]{"FileA","FileB", "DirC", "DirC/FileD", "DirE", "UndefF", "DirC/UndefF", "DirE/UndefF"/*, "DirE/UndefG/UndefH/UndefI"*/};
    }

    protected JDBCResourceStoreProperties mockConfig(boolean enabled, boolean init) {
        JDBCResourceStoreProperties config = createMock(JDBCResourceStoreProperties.class);

        expect(config.isInitDb()).andStubReturn(init);
        expect(config.isEnabled()).andStubReturn(enabled);
        expect(config.isImport()).andStubReturn(init);

        support.stubConfig(config);

        return config;
    }

    protected DataSource testDataSource() throws Exception {
        return support.getDataSource();
    }

    public AbstractJDBCResourceTheoryTest() {
        super();
    }

    protected void standardData() throws Exception {
        support.initialize();

        support.addFile("FileA", 0, "FileA Contents".getBytes());
        support.addFile("FileB", 0, "FileB Contents".getBytes());
        int c = support.addDir("DirC", 0);
        support.addFile("FileD", c, "FileD Contents".getBytes());
        support.addDir("DirE", 0);
    }

    Integer getInt(ResultSet rs, String column) throws Exception {
        int i = rs.getInt(column);
        if(rs.wasNull()) return null;
        return i;
    }

    @After
    public void cleanUp() throws Exception {
        support.close();
    }
}

Postgres测试用例只需插入Postgres测试支持模块,并使用它来初始化测试框架和正在测试的资源库。它目前连接到每个测试的每个数据点的数据库,尽管我打算修复它。

public class PostgresJDBCResourceTheoryTest extends AbstractJDBCResourceTheoryTest {

    JDBCResourceStore store;

    @Override
    protected Resource getResource(String path) throws Exception{
        return store.get(path);
    }

    @Before
    public void setUp() throws Exception {
        support = new PostgresTestSupport();

        standardData();

        JDBCResourceStoreProperties config = mockConfig(true, false);
        replay(config);

        store = new JDBCResourceStore(support.getDataSource(), config);
    }
}

还有一个H2实现,测试的工作方式相同但使用内存H2数据库。

3 个答案:

答案 0 :(得分:0)

我不知道你的代码是什么样的,我做了一系列假设并创建了一个似乎支持你的意图的例子。

import org.junit.experimental.theories.*;

@org.junit.runner.RunWith(Theories.class)
public class TheoryTest {
    // this is one possible way to store the setting:
    private static ThreadLocal<Boolean> postgresConnected = new ThreadLocal<>();
    static interface Under {}
    static class Over implements Under {}
    static class Through implements Under {}
    static { postgresConnected.set(true); } // this logic belongs somewhere else

    @DataPoints
    public static Under[] underData() {
        if (postgresConnected.get())
            return new Under[] { new Over(), new Through() };
        return new Under[] { new Over() };
    }

    @Theory
    public void testUnder(Under under) {
        System.out.println(under.getClass());
        org.junit.Assert.assertNotNull(under);
    }
}

如果这不能满足您的需求,请发布更多细节,我会看到我能想出的内容。

答案 1 :(得分:0)

一种解决方案是使用ThreadLocal。

这是一种允许您设置特定Thread变量的机制,在您的情况下,您可以使用ThreadLocal提供Postgres连接,并根据连接的存在来了解是否运行特定测试。

这篇文章包含一个如何使用Java ThreadLocal

的简单示例

答案 2 :(得分:0)

鉴于您的更新问题,以下是您的错误:您尝试使用在@DataPoints(设置)代码之前定义的@Before。正如我在评论中所说,你正在与JUnit架构作斗争。 @DataPoints是静态定义的,但事后您的数据库连接是按测试定义的。正如我原来的回答所示,@DataPoints根据连接状态而变化(绝对?)必不可少,这在您的代码示例中不会发生。

尽管如此,我还有一个有效的解决方案。由于理论需要至少一个有效数据点,因此我创建了一个始终有效的回退,并在没有连接时使用它。代码以相反的顺序发布,但确实编译并运行(当从问题中恢复剪辑时)。

public class PostgresJDBCResourceTheoryTest extends AbstractJDBCResourceTheoryTest {
    static JDBCResourceStore store;

    protected Resource getResource(String path) throws Exception {
        if (path == "AlwaysValidResource")
            return ALWAYS_VALID;
        return store.get(path);
    }

    @BeforeClass
    public static void setUp() throws Exception {
        . . .
        store = new JDBCResourceStore(support.getDataSource(), config);
        connect();
    }

    @AfterClass
    public static void tearDown() {
        disconnect();
    }
}

public abstract class AbstractJDBCResourceTheoryTest extends ResourceTheoryTest {
    private static ThreadLocal<Boolean> connected = new ThreadLocal<>();

    @DataPoints
    public static String[] testPaths() {
        if (connected.get() == null || ! connected.get())
            return new String[]{"AlwaysValidResource"};
        return new String[]{"FileA", "FileB", "DirC", "DirC/FileD", "DirE", "UndefF", "DirC/UndefF", "DirE/UndefF"/*, "DirE/UndefG/UndefH/UndefI"*/};
    }

    static void connect() {
        connected.set(true);
    }
    static void disconnect() {
        connected.set(false);
    }
  }

@RunWith(Theories.class)
public abstract class ResourceTheoryTest {
    public static final Resource ALWAYS_VALID = new AlwaysValidResource();
    . . .
}
相关问题