2012-04-06 114 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); 
        ... 
    }