如何使用默认构造函数伪造初始上下文

2022-09-03 16:37:36

我试图在一些古老的java代码中做一些单元测试(没有接口,没有抽象等)。

这是一个使用ServletContext的servlet(我假设它是由Tomcat设置的),并且它具有在web.xml/context.xml文件中设置的数据库信息。现在,我已经想出了如何制作一个假的ServletContext,但是代码已经

 InitialContext _ic = new InitialContext();

到处都是(所以更换它是不可行的)。我需要找到一种方法来使默认的InitialContext()能够在不引发异常的情况下执行。_ic.lookup(val)

我假设上下文.xml以某种方式加载,但是这种魔力是如何工作的,我正在画一个空白。有人有什么想法吗?


答案 1

利用使用SPI来处理其创建的事实。您可以通过创建 的实现并通过 system 属性 () 将其传递给测试来挂钩到其生命周期。这比听起来更简单。InitialContextjavax.naming.spi.InitialContextFactoryjavax.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

在命令行输出上(在 eclipse 中易于设置)。我喜欢Mockito的嘲笑和茬茬。如果你处理大量的遗留代码,我也会推荐Micheal Feather的《有效使用遗留代码》。这一切都关于如何在程序中查找接缝,以便隔离特定部分进行测试。This is my object!!


答案 2

以下是我为单元测试设置内部上下文的解决方案。首先,我将以下测试依赖项添加到我的项目中:

<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。


推荐