[英]How to fake InitialContext with default constructor
全部,
我正在嘗試在一些陳舊的 java 代碼中進行一些單元測試(沒有接口,沒有抽象等)
這是一個使用 ServletContext 的 servlet(我假設它是由 Tomcat 設置的)並且它在 web.xml/context.xml 文件中設置了數據庫信息。 現在,我已經想出了如何制作一個 Fake ServletContext,但是代碼有
InitialContext _ic = new InitialContext();
到處都是(所以更換它是不可行的)。 我需要找到一種方法來使默認的 InitialContext() 能夠執行_ic.lookup(val)
而不會引發異常。
我假設有某種方式可以加載 context.xml,但是這個魔法是如何工作的,我正在畫一個空白。 誰有想法?
利用InitialContext
使用 SPI 來處理其創建這一事實。 您可以通過創建javax.naming.spi.InitialContextFactory
的實現並通過系統屬性javax.naming.factory.initial
( Context.INTITIAL_CONTEXT_FACTORY
) 將其傳遞給您的測試來掛鈎其生命周期。 它比聽起來簡單。
鑒於此 class:
public class UseInitialContext {
public UseInitialContext() {
try {
InitialContext ic = new InitialContext();
Object myObject = ic.lookup("myObject");
System.out.println(myObject);
} catch (NamingException e) {
e.printStackTrace();
}
}
}
而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!!
(很容易在日食中設置)。 我喜歡Mockito用於 mocking 和存根。 如果您處理大量遺留代碼,我還推薦 Micheal Feather 的Working Effectively with Legacy Code 。 這一切都是關於如何在程序中找到接縫,以便隔離特定的部分進行測試。
這是我為單元測試設置 Inintial Context 的解決方案。 首先,我將以下測試依賴項添加到我的項目中:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>catalina</artifactId>
<version>6.0.33</version>
<scope>test</scope>
</dependency>
然后我用下面的代碼創建了一個 static 方法:
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);
}
最后,在我的每個測試 class 中,我添加了一個調用 setupInitialContext 的 @BeforeClass 方法。
之前嘗試設置系統變量:
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
今天我遇到了同樣的問題(我們不能使用 PowerMock)並以這種方式解決了它:
不要在構造函數中查找,因此當您在 object 上調用 @InitMock 時,構造函數還不需要上下文。
創建一個在需要時檢索服務 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); ... }
你考慮過 mockito嗎?
很簡單:
InitialContext ctx = mock(InitialContext.class);
順便說一句,如果您選擇使用模擬,我也建議您閱讀這篇文章: http://martinfowler.com/articles/mocksArentStubs.html
不使用外部庫的窮人獨立實現:
public class myTestClass {
public static class TestContext extends InitialContext {
public TestContext() throws NamingException {
super(true /*prevents initialization*/);
}
static Object someExpectedValue = "the expected string or object instance";
/*override the method(s) called by the legacy program on _ic, check the parameter and return the wanted value */
public Object lookup(String name) throws NamingException {
return name != null && name.equals("theValueOfVal") ? someExpectedValue : null;
}
}
public static class TestInitialContextFactory implements InitialContextFactory {
public Context getInitialContext(Hashtable<?, ?> arg0) throws NamingException {
return new TestContext();
}
}
public static void main(String[] args) throws SQLException {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "the.package.myTestClass$TestInitialContextFactory");
/*now call the legacy logic to be tested*/
...
您可以在lookup
方法的覆蓋中使用一個switch
,以返回整個遺留程序中傳遞給_ic.lookup(val)
的每個不同val
值的預期值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.