我有一个使用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数据库。
答案 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();
. . .
}