2012-04-06 40 views
12

所有,如何使用默認的構造函數假的InitialContext

我試圖做一些過時的Java代碼中的一些單元測試(無接口,沒有抽象等)

這是一個servlet使用一個ServletContext(我假設它是由Tomcat設置的),它有數據庫信息在web.xml/context.xml文件中設置。現在,我已經想通了如何做一個假的ServletContext,但該代碼

InitialContext _ic = new InitialContext(); 

所有的地方(所以它不是可行,取代它)。我需要找到一種方法來使默認的InitialContext()能夠在不拋出異常的情況下執行_ic.lookup(val)

我假設有一些方式,context.xml正在加載,但如何神奇的作品,我畫了一個空白。有人有主意嗎?

+0

僅僅因爲它發生很多並不意味着它絕對不可取代它。嘿,即使只是改變使用靜態工廠方法將允許*更多*可測試性(雖然它顯然不如一些替代品)。 – 2012-04-06 15:19:49

回答

3

您可以使用PowerMock來模擬InitialContext的構造並控制其行爲。構造器嘲笑記錄在here

PowerMock測試可能非常混亂和複雜,重構通常是更好的選擇。

+0

+1。 PowerMock功能強大,但它肯定會引起足夠的麻煩,所以重構更受歡迎。 – AHungerArtist 2012-04-06 15:36:25

+0

我認爲這是我想要做的最好的解決方案。謝謝! – Austin 2012-04-08 00:40:24

+0

避免PowerMock,除非你沒有其他選擇留給你...大量的文章解釋了爲什麼。 – 2017-07-26 06:44:32

4

嘗試之前,設置系統變量:

System.setProperty(Context.INITIAL_CONTEXT_FACTORY, 
     "org.apache.naming.java.javaURLContextFactory"); 
System.setProperty(Context.URL_PKG_PREFIXES, 
     "org.apache.naming"); 
InitialContext ic = new InitialContext(); 

如果您正在使用JUnit,按照此文檔:https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit

+1

這給出了'java.lang.ClassNotFoundException:org.apache.naming.java.javaURLContextFactory' – 2013-02-11 14:06:58

+1

你應該在你的項目中擁有tomcat環境。您必須添加tomcat jar – 2013-02-12 09:10:41

+0

因此,如果我使用JBoss,我想我必須使用JBoss環境。謝謝! – 2013-02-12 11:36:15

6

這裏是我的解決方案來建立Inintial語境。我的單元測試。首先,我增加了以下的測試依賴於我的項目:

<dependency> 
    <groupId>org.apache.tomcat</groupId> 
    <artifactId>catalina</artifactId> 
    <version>6.0.33</version> 
    <scope>test</scope> 
</dependency> 

然後,我創建了下面的代碼靜態方法:

public static void setupInitialContext() throws Exception { 
    System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory"); 
    System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming"); 
    InitialContext ic = new InitialContext(); 
    ic.createSubcontext("jdbc"); 
    PGSimpleDataSource ds = new PGSimpleDataSource(); 
    ds.setDatabaseName("postgres"); 
    ds.setUser("postgres"); 
    ds.setPassword("admin"); 
    ic.bind("jdbc/something", ds); 
} 
在我的每個測試類的

最後我想補充的@BeforeClass方法,調用setupInitialContext。

+0

與此https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit 非常好 – hephestos 2013-11-06 15:06:35

35

利用這一InitialContext使用的SPI來處理其創作的事實。您可以通過創建javax.naming.spi.InitialContextFactory的實現並將其通過系統屬性javax.naming.factory.initialContext.INTITIAL_CONTEXT_FACTORY)傳遞給您的測試來鎖定其生命週期。它比聽起來更簡單。

鑑於這一類:

public class UseInitialContext { 

    public UseInitialContext() { 
     try { 
      InitialContext ic = new InitialContext(); 
      Object myObject = ic.lookup("myObject"); 
      System.out.println(myObject); 
     } catch (NamingException e) { 
      e.printStackTrace(); 
     } 
    } 


} 

這IMPL的InitialContextFactory

public class MyInitialContextFactory implements InitialContextFactory { 

    public Context getInitialContext(Hashtable<?, ?> arg0) 
      throws NamingException { 

     Context context = Mockito.mock(Context.class); 
     Mockito.when(context.lookup("myObject")).thenReturn("This is my object!!"); 
     return context; 
    } 
} 

在JUnit測試創建的UseInitialContext的實例與

-Djava.naming.initial.factory=initial.context.test.MyInitialContext 

在命令行上輸出This is my object!!(易於在eclipse中設置)。我喜歡Mockito嘲笑和殘留。如果您處理大量遺留代碼,我還會推薦Micheal Feather的Working Effectively with Legacy Code。這是關於如何在程序中找到接縫以便隔離特定的測試片。

+0

而不是'java.naming.initial.factory',我認爲正確的系統屬性是'java.naming.factory.initial'。至少在java中6.感謝帖子! – marciopd 2013-07-12 15:28:02

+3

'java.naming.factory.initial'也適用於java 7. – 2013-11-13 10:32:59

+0

FWIW,我在一段時間後寫了一個非常類似的問題,我做了這個[使用JUnit'TestTule'來處理設置和拆解](http://stackoverflow.com/a/17083737/116639)。 – 2013-11-13 12:22:37

1

今天我遇到了同樣的問題(我們不能用戶PowerMock),並解決了它這種方式:

  1. ,當你在對象上調用@InitMock不要在構造函數中查找的話,構造函數不需要上下文。

  2. 需要像時,用於檢索所述服務bean創建的方法 「的getService()serviceMethod(PARAM,PARAM ...)」:

/* Class ApplicationResourceProvider */ 

    /* We can mock this and set it up with InjectMocks */ 
    InitialContext ic; 

    /* method hiding the lookup */ 
    protected ApplicationService getService() throws NamingException { 
     if(ic == null) 
      ic = new InitialContext(); 
     return (ApplicationService)ic.lookup("java:global/defaultApplicationLocal"); 
    } 
  • 在測試中,將其設置:
  • @Mock 
    ApplicationService applicationServiceBean; 
    
    @Mock 
    InitialContext ic; 
    
    @InjectMocks 
    ApplicationResourceProvider arp; 
    
    @Before 
    public void setUp() throws Exception { 
        MockitoAnnotations.initMocks(this); 
        when(ic.lookup(anyString())).thenReturn(applicationServiceBean); 
        ... 
    }