2014-02-24 31 views
4

我有一個抽象的測試用例,使用測試接口的Theories runner。接口的每個實現都具有測試用例的具體實現,其中一個實現使用Postgres。我想讓測試用例只在連接到Postgres數據庫時纔會運行,否則將被忽略。如何在使用Theories時在jUnit中獲得條件執行?

我不能使用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

正如我在我的回答中演示的,您的@ DataPoints方法必須是您檢查連接狀態的點。鑑於你的更新代碼,你需要有一個永久有效的資源來測試。 你想要做的正確解決方案是實現你自己的跑步者。我說的是正確的,因爲這就是你如何做到這一點,而不是試圖對抗JUnit的架構。但是這可能比你想要解決的更麻煩。 – ngreen

回答

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); 
    } 
} 

如果這不符合您的需求,請發佈一些更多的細節,我會看看我能想出什麼。

+0

謝謝,我不認爲這將工作,因爲我有不同的實現抽象測試用例類的多個子類。有些在單獨的模塊中,可能並不總是被構建的,並且當它們被構建時,Postgres實例可能不可用,但其餘的測試用例仍然需要運行。當我有更多時間時,我會更新這個問題。 – smithkm

+0

是的,這個問題中的更多細節會有幫助,因爲我認爲所有這些情況都可以處理。 – ngreen

0

一個解決方案是使用ThreadLocal。

它是一種允許您設置特定線程變量的機制,在您的情況下,您可以使用ThreadLocal傳遞Postgres連接,並根據是否存在連接來知道是否運行特定測試。

這篇文章包含如何使用Java ThreadLocal

+0

它沒有得到一個Postgres連接到存在問題的測試,它將在該連接不可用時關閉測試。通常情況下,我會使用假設,但理論跑步者失敗,如果一個理論不符合所有數據點的假設,而不是忽略正常情況下的測試。理想情況下,我想通過Theories測試運行器獲得相同的運行時忽略行爲的方法。 – smithkm

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(); 
    . . . 
} 
相關問題