如何在单元测试中模拟InitialContext构造函数

Anu*_*hna 8 java junit unit-testing ejb

当我尝试模拟以下方法(方法是使用业务逻辑的远程EJB调用)进行Junit测试时,它给出了javax.naming.NoInitialContextException

private void someMethod(int id1, int id2, HashMap map){
    ......some code........

    Context ctx = new InitialContext();
    Object ref = ctx.lookup("com.java.ejbs.MyEJB");

    EJBHome ejbHome = (EJBHome)PortableRemoteObject.narrow(ref, EJBHome.class);
    EJBBean ejbBean = (EJBBean)PortableRemoteObject.narrow(ejbHome.create(), EJBBean.class);
    ejbBean.someMethod(id1,name);

    .......some code.......}
Run Code Online (Sandbox Code Playgroud)

我对上述方法的单元测试

@Test
public void testsomeMethod() throws Exception {

    .......setting initial code...
    //Mock context and JNDI

    InitialContext cntxMock = PowerMock.createMock(InitialContext.class);
    PowerMock.expectNew(InitialContext.class).andReturn(cntxMock);
    expect(cntxMock.lookup("com.java.ejbs.MyEJB")).andReturn(refMock);               

    ..........some code..........

    PowerMock.replayAll();
    Whitebox.invokeMethod(ObjectOfsomeMethodClass, "someMethod", id1, id2, map);


}
Run Code Online (Sandbox Code Playgroud)

Whitebox.invokeMethod(ObjectOfsomeMethodClass,"someMethod",id1,id2,map)方法调用它时,它会给出以下异常.

javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:645)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:325)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
Run Code Online (Sandbox Code Playgroud)

我相信,虽然我们在test方法中模拟了Context,但在调用Whitebox.invokeMethod(ObjectOfsomeMethodClass,"someMethod",id1,id2,map)方法时它并没有使用mock对象,而是试图调用Context ctx = new InitialContext(); 原始方法中的方法(someMethod).

Jar*_*zek 8

手工制造

正如InitialContextdoc所说,您可以InitialContext使用java.naming.factory.initial系统属性为对象提供自己的工厂.当代码在应用程序服务器内运行时,系统属性由服务器设置.在我们的测试中,我们提供了自己的JNDI实现.

这是我的Mockito唯一的解决方案:我定义了一个自定义InitialContextFactory类,它返回一个模拟InitialContext.您可以根据需要自定义模拟,可能会在lookup调用时返回更多模拟.

public class PlainTest {
  @Mock InitialContextFactory ctx;
  @InjectMocks Klasa1 klasa1;

  public static class MyContextFactory implements InitialContextFactory
  {
    @Override
    public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
      ConnectionFactory mockConnFact = mock(ConnectionFactory.class);
      InitialContext mockCtx = mock(InitialContext.class);
      when(mockCtx.lookup("jms1")).thenReturn(mockConnFact);
      return mockCtx;
    }
  }

  @Before
  public void setupClass() throws IOException
  {
    MockitoAnnotations.initMocks(this);
    System.setProperty("java.naming.factory.initial",
      this.getClass().getCanonicalName() + "$MyContextFactory");
  }
Run Code Online (Sandbox Code Playgroud)

春天(编辑添加)

如果你不介意利用Spring Framework进行测试,那么这是他们的简单解决方案:SimpleNamingContextBuilder:

SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
DataSource ds = new DriverManagerDataSource(...);
builder.bind("java:comp/env/jdbc/myds", ds);
builder.activate();
Run Code Online (Sandbox Code Playgroud)

可以把它放入@Before@BeforeClass.之后activate(),jndi数据将从spring dummy中拉出来.


小智 1

您可以重构代码并在新方法中提取上下文的初始化。

private void someMethod(int id1, int id2, HashMap map){
    ......some code........

    Context ctx = getInitialContext();
    Object ref = ctx.lookup("com.java.ejbs.MyEJB");

    EJBHome ejbHome = (EJBHome)PortableRemoteObject.narrow(ref, EJBHome.class);
    EJBBean ejbBean = (EJBBean)PortableRemoteObject.narrow(ejbHome.create(), EJBBean.class);
    ejbBean.someMethod(id1,name);

    .......some code.......}
Run Code Online (Sandbox Code Playgroud)

你的测试代码将是这样的:

Context mockContext = mock(Context.class);
doReturn(mockContext).when(yourclass).getInitalContext(); 
...... some code....
Run Code Online (Sandbox Code Playgroud)