Java 如何使用默认构造函数伪造InitialContext
全部, 我试图用一些古老的java代码(没有接口,没有抽象,等等)进行一些单元测试 这是一个使用ServletContext(我假设它是由Tomcat设置的)的servlet,它的数据库信息设置在web.xml/context.xml文件中。现在,我已经知道了如何创建一个伪ServletContext,但是代码已经完成了Java 如何使用默认构造函数伪造InitialContext,java,unit-testing,initial-context,Java,Unit Testing,Initial Context,全部, 我试图用一些古老的java代码(没有接口,没有抽象,等等)进行一些单元测试 这是一个使用ServletContext(我假设它是由Tomcat设置的)的servlet,它的数据库信息设置在web.xml/context.xml文件中。现在,我已经知道了如何创建一个伪ServletContext,但是代码已经完成了 InitialContext _ic = new InitialContext(); 到处都是(因此更换它是不可行的)。我需要找到一种方法,使默认的InitialConte
InitialContext _ic = new InitialContext();
到处都是(因此更换它是不可行的)。我需要找到一种方法,使默认的InitialContext()能够在不引发异常的情况下执行\u ic.lookup(val)
我假设context.xml是以某种方式加载的,但这一魔法是如何发挥作用的,我对此一无所知。有人有什么想法吗?您可以使用它来模拟InitialContext的构造并控制其行为。构造函数模拟是有文档记录的
PowerMock测试可能会非常混乱和复杂,重构通常是一个更好的选择。在以下操作之前尝试设置系统变量:
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,请遵循以下文档:以下是我为单元测试设置重要上下文的解决方案。首先,我将以下测试依赖项添加到我的项目中:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>catalina</artifactId>
<version>6.0.33</version>
<scope>test</scope>
</dependency>
最后,在我的每个测试类中,我添加了一个@BeforeClass方法,该方法调用setupInitialContext。您考虑过吗
这很容易:
InitialContext ctx=mock(InitialContext.class)
顺便说一句,如果您选择使用mock,我建议您也阅读本文:利用
InitialContext
使用SPI来处理其创建。您可以通过创建javax.naming.spi.InitialContextFactory
的实现并通过系统属性javax.naming.factory.initial
(Context.initial\u Context\u 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
的实现:
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;
}
}
在命令行输出
上,这是我的对象代码>(易于在eclipse中设置)。我喜欢嘲笑和打短棍。如果你处理很多遗留代码,我也推荐Micheal Feather的。所有这些都是关于如何在程序中找到接缝,以便隔离特定的测试部分 今天,我遇到了同样的问题(我们无法使用PowerMock),并通过以下方式解决了这个问题:
不要在构造函数中查找,这样当您在对象上调用@InitMock时,构造函数还不需要上下文
创建一个在需要时检索服务bean的方法,如“getService().serviceMethod(param,param…)”:
在测试中,设置它:
穷人不使用外部库的独立实现:
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*/
...
公共类myTestClass{
公共静态类TestContext扩展了InitialContext{
public TestContext()引发NamingException{
super(true/*防止初始化*/);
}
静态对象someExpectedValue=“预期的字符串或对象实例”;
/*重写_ic上的遗留程序调用的方法,检查参数并返回所需的值*/
公共对象查找(字符串名称)引发NamingException{
返回name!=null&&name.equals(“theValueOfVal”)?someExpectedValue:null;
}
}
公共静态类TestInitialContextFactory实现InitialContextFactory{
公共上下文getInitialContext(哈希表arg0)引发NamingException{
返回新的TestContext();
}
}
公共静态void main(字符串[]args)引发SQLException{
System.setProperty(Context.INITIAL\u Context\u FACTORY,“the.package.myTestClass$TestInitialContextFactory”);
/*现在调用要测试的遗留逻辑*/
...
您可以在查找
方法的覆盖中使用开关
,返回传递给查找(val)的每个不同val
值的预期值
在整个遗留程序中。仅仅因为它经常出现并不意味着更换它肯定是不可行的。见鬼,即使只是更改为使用静态工厂方法也会允许更高的可测试性(尽管它显然不如某些替代方法好).1.PowerMock功能强大,但它确实引起了足够多的麻烦,因此更倾向于使用重构。我认为这是我正在尝试的最佳解决方案。谢谢!除非您没有其他选择,否则请避免使用PowerMock…大量文章解释了原因。这给出了java.lang.ClassNotFoundException:org.apache.naming.java.javaURLContextFactory
您的项目中应该有tomcat环境。如果我使用JBoss,您必须添加tomcat jarsSo。我想我必须使用JBoss环境。谢谢!oracle博客的链接被断开,而不是“java.naming.initial.factory”,我认为正确的系统属性是“java.naming.factory.initial”。在lejava 6中的ast。感谢您的帖子!java.naming.factory.initial也适用于java 7.FWIW,在此之后,我为一个非常类似的问题写了一个非常类似的答案,我做了这个。Context.initial\u Context\u factory,所以您不想记住初始上下文工厂环境变量name的实际字符串值非常好
-Djava.naming.initial.factory=initial.context.test.MyInitialContext
/* 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);
...
}
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*/
...