Java 使用理论时,如何在jUnit中获得条件执行?

Java 使用理论时,如何在jUnit中获得条件执行?,java,junit,Java,Junit,我有一个抽象的测试用例,它使用理论运行程序测试一个接口。接口的每个实现都有一个测试用例的具体实现,其中一个实现使用Postgres。我希望仅当连接到Postgres数据库的连接实际可用时才运行该测试用例,否则将被忽略 我不能使用假设,因为理论测试运行程序将失败,如果一个理论的所有数据点都不符合他们的假设 我正在测试一个ResourceStore,它本质上是一个简单的文件系统。它获取路径并返回资源对象,这些对象可能由文件系统或其他类似Postgres的东西支持。我使用理论来测试返回的资源是否遵循某

我有一个抽象的测试用例,它使用
理论
运行程序测试一个接口。接口的每个实现都有一个测试用例的具体实现,其中一个实现使用Postgres。我希望仅当连接到Postgres数据库的连接实际可用时才运行该测试用例,否则将被忽略

我不能使用
假设
,因为
理论
测试运行程序将失败,如果一个理论的所有数据点都不符合他们的假设

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

基类如下所示(导入和大多数特定测试)

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

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数据库。

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

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);
    }
}
import org.junit.experimental.Theory.*;
@org.junit.runner.RunWith(theory.class)
公共类理论测试{
//这是存储设置的一种可能方式:
private static ThreadLocal postgresConnected=new ThreadLocal();
{}下的静态接口
{}下实现上的静态类
通过{}下的实现的静态类
静态{postgresConnected.set(true);}//此逻辑属于其他地方
@数据点
[]underData()下的公共静态{
if(postgresConnected.get())
在[]{new Over(),new Through()}下返回new;
在[]{new Over()}下返回new;
}
@理论
公共无效测试下(下){
System.out.println(在.getClass()下);
org.junit.Assert.assertNotNull(在下);
}
}

如果这不符合您的需要,请发布更多详细信息,我会看看我能想出什么。

一个解决方案是使用ThreadLocal

它是一种允许您设置特定线程变量的机制,在您的情况下,您可以使用ThreadLocal传递Postgres连接,并根据连接的存在情况知道是否运行特定测试


这篇文章包含了一个简单的例子,说明了如何使用鉴于您更新的问题,以下是您的错误之处:您试图使用
@DataPoints
,这些数据点是在
@before
(设置)代码之前定义的。正如我在评论中所说,您正在与
JUnit
体系结构作斗争
@DataPoints
是静态定义的,但您的数据库连接是在每次测试之后定义的。如我的原始答案所示,
@DataPoints
根据连接状态的不同而变化是(绝对?)必要的,这在您的代码示例中不会发生

不过,我有一个可行的解决办法。因为理论至少需要一个有效的数据点,所以我创建了一个始终有效的回退,并在没有连接时使用它。代码以相反的顺序发布,但确实编译并运行(当从问题中恢复剪报时)

公共类PostgresJDBCResourceTest扩展了AbstractJDBCResourceTheoryTest{
静态JDBCResourceStore;
受保护的资源getResource(字符串路径)引发异常{
如果(路径==“AlwaysValidResource”)
返回总是有效的;
返回store.get(路径);
}
@课前
public static void setUp()引发异常{
. . .
store=新的JDBCResourceStore(support.getDataSource(),config);
connect();
}
@下课
公共静态void拆卸(){
断开连接();
}
}
公共抽象类AbstractJDBCResourceTheoryTest扩展了ResourceTheoryTest{
private static ThreadLocal connected=new ThreadLocal();
@数据点
公共静态字符串[]testpath(){
if(connected.get()==null | |!connected.get())
返回新字符串[]{“AlwaysValidResource”};
返回新字符串[]{“FileA”、“FileB”、“DirC”、“DirC/field”、“DirE”、“undef”、“DirC/undef”、“DirE/undef”/*、“DirE/UndefG/UndefH/UndefI”*/};
}
静态void connect(){
已连接。设置(true);
}
静态无效断开连接(){
已连接。设置(false);
}
}
@RunWith(理论课)
公共抽象类ResourceTheoryTest{
公共静态最终资源始终有效=新的AlwaysValidResource();
. . .
}

谢谢,但我认为这不起作用,因为我有一个抽象测试用例类的多个子类用于不同的实现。有些是在独立的模块中,这些模块可能并不总是被构建的,当它们被构建时,Postgres实例可能不可用,但是其余的测试用例仍然需要运行。当我有更多的时间时,我会更新这个问题。是的,问题中的更多细节会有所帮助,因为我认为所有这些情况都可以处理。问题不在于是否存在与测试的Postgres连接,而在于何时关闭测试
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);
    }
}
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();
    . . .
}