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

7ivaypg9  于 2023-08-05  发布在  其他
关注(0)|答案(5)|浏览(116)

当我试图模拟下面的方法(方法使用业务逻辑的远程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.......}

字符串
以上方法的单元测试

@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);

}

**Whitebox.invokeMethod(ObjectOfsomeMethodClass,“someMethod”,id 1,id 2,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)


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

piv4azn7

piv4azn71#

手工制作

正如InitialContext doc所说,您可以使用java.naming.factory.initial系统属性为InitialContext对象提供自己的工厂。当代码在应用服务器内部运行时,系统属性由服务器设置。在我们的测试中,我们提供了自己的 JNDI 实现。
以下是我的 Mockito 唯一解决方案:我定义了一个自定义的InitialContextFactory类,它返回InitialContext的模拟。您可以根据自己的意愿定制mock,可能是为了在lookup调用上返回更多的mock。

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");
  }

字符串

Spring版本6之前(edit 添加)

另一个编辑:这是5.2中的deprecated,在6.0中被删除。
如果您不介意利用Spring Framework进行测试,以下是他们的简单解决方案:SimpleNamingContextBuilder

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


可以将其放入@Before@BeforeClass中。在activate()之后,jndi数据将从spring dummy中提取。

t5zmwmid

t5zmwmid2#

添加到Jarekczek's答案(谢谢!虽然这是一个老问题,但我想分享我的版本,以防它对某人有帮助。我遇到了同样的问题,人们可能只想在IntialContextFactory实现类中模拟IntialContext,在其他测试或基本测试类中使用这个模拟对象以避免重复是一个更好的主意。

public class MyContextFactory implements InitialContextFactory { 
    // Poor Singleton approach. Not thread-safe (but hope you get the idea)
    private static InitialContext mockInitialContext;
    @Override
    public Context getInitialContext(Hashtable<?,?> hshtbl) throws NamingException {
        if(mockInitialContext == null) {
            mockInitialContext = mock(InitialContext.class);
        }
        return mockInitialContext;
    }
}

public class TestClass {
    private DataSource mockDataSource;
    private Connection mockConnection;

    protected void mockInitialContext() throws NamingException, SQLException {
        System.setProperty("java.naming.factory.initial", "com.wrapper.MyContextFactory");

        InitialContext mockInitialContext = (InitialContext) NamingManager.getInitialContext(System.getProperties());
        mockDataSource = mock(DataSource.class);
        mockConnection = mock(Connection.class);

        when(mockInitialContext.lookup(anyString())).thenReturn(mockDataSource);
        when(mockDataSource.getConnection()).thenReturn(mockConnection);

        try {
            when(mockDataSource.getConnection()).thenReturn(mockConnection);
        } catch (SQLException ex) {
            Logger.getLogger(CLASSNAME).log(Level.SEVERE, null, ex);
        }
    }
}

字符串
采用这种方法的原因是,如果有人希望以不同的方式使用数据源或JNDI提供的任何其他资源进行不同的测试,您可以遵循这种方法。应该只为IntialContext创建一个示例,除非一个多线程测试试图同时访问它(不知道为什么要这样做!))。该示例可以在任何地方使用,以获取所需的JNDI对象并按需使用它们。
希望这对你有帮助!

  • “确保你在每顿饭前洗手,在调试健康生活方式时避免System.out.println”*
u3r8eeie

u3r8eeie3#

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

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.......}

字符串
你的测试代码如下所示:

Context mockContext = mock(Context.class);
doReturn(mockContext).when(yourclass).getInitalContext(); 
...... some code....

nue99wik

nue99wik4#

截至目前(PowerMock 1.7.4)

使用PowerMockito.mock(InitialContext.class)而不是PowerMockito.createMock(InitialContext.class)创建模拟

@Test
public void connectTest() {
    String jndi = "jndi";
    InitialContext initialContextMock = PowerMockito.mock(InitialContext.class);
    ConnectionFactory connectionFactoryMock = PowerMockito.mock(ConnectionFactory.class);

    PowerMockito.whenNew(InitialContext.class).withNoArguments().thenReturn(initialContextMock);
    when(initialContextMock.lookup(jndi)).thenReturn(connectionFactoryMock);  

    ...

    // Your asserts go here ...
}

字符串
不要手动创建InitialContext,而是让PowerMock为您创建。另外,不要创建PowerMock需要对象的间谍。这意味着您需要创建InitialContext示例。

1mrurvl1

1mrurvl15#

定义以下自定义类

public static class CustomInitialContext extends InitialContext {

    Hashtable<String, Object> ic = new Hashtable<>();

    public CustomInitialContext() throws NamingException {
        super(true);
    }

    public void bind(String name, Object object) {
        ic.put(name, object);
    }

    public Object lookup(String name) throws NamingException {
        return ic.get(name);
    }
}

public static class CustomInitialContextFactory implements InitialContextFactory {
    static InitialContext ic;

    public CustomInitialContextFactory() {
        if (ic == null) {
            try {
                ic = new CustomInitialContext();
            } catch (NamingException e) {
                e.printStackTrace();
            }
        }
    }

    public Context getInitialContext(Hashtable<?, ?> arg0) throws NamingException {
        return ic;
    }
}

public static class CustomInitialContextFactoryBuilder implements InitialContextFactoryBuilder {

    @Override
    public InitialContextFactory createInitialContextFactory(Hashtable<?, ?> environment) throws NamingException {
        return new CustomInitialContextFactory();
    }

}

字符串
并声明工厂为

NamingManager.setInitialContextFactoryBuilder(new CustomInitialContextFactoryBuilder());

相关问题