所有
我正在尝试在一些古老的java代码中进行一些单元测试(没有接口,没有抽象等)
这是一个使用ServletContext的servlet(我假设它是由Tomcat设置的),并且它在web.xml / context.xml文件中设置了数据库信息。现在,我已经弄清楚如何制作一个假的ServletContext,但代码有
InitialContext _ic = new InitialContext();
到处都是(因此替换它是不可行的)。我需要找到一种方法来使默认的InitialContext()能够执行_ic.lookup(val)
而不会抛出异常。
我假设有一些方法可以加载context.xml,但是这种魔法是如何工作的,我正在画一个空白。有人有什么想法吗?
答案 0 :(得分:35)
利用InitialContext
使用SPI来处理其创建的事实。您可以通过创建javax.naming.spi.InitialContextFactory
的实现并通过系统属性javax.naming.factory.initial
(Context.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();
}
}
}
InitialContextFactory
的这个impl:
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。这一切都是关于如何在程序中找到接缝以隔离特定的测试件。
答案 1 :(得分:6)
这是我为单元测试设置Inintial Context的解决方案。首先,我将以下测试依赖项添加到我的项目中:
<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);
}
最后在我的每个测试类中,我添加了一个调用setupInitialContext的@BeforeClass方法。
答案 2 :(得分: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
答案 3 :(得分:3)
答案 4 :(得分:1)
今天我遇到了同样的问题(我们无法使用PowerMock)并以这种方式解决了这个问题:
不要在构造函数中查找,因此当您在对象上调用@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); ... }
答案 5 :(得分:0)
您考虑过mockito吗?
这很简单:
InitialContext ctx = mock(InitialContext.class);
顺便说一句,如果您选择使用模拟,我建议您也阅读这篇文章:http://martinfowler.com/articles/mocksArentStubs.html
答案 6 :(得分:0)
一个穷人的独立实现,不使用任何外部库:
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*/
...
您可以在switch
方法的替代中使用lookup
,以返回在整个旧版程序中传递给val
的每个不同_ic.lookup(val)
值的期望值。 / p>