@EJB注释不适用于EJB应用程序中的初始化bean

Rox*_*Rox 0 java junit unit-testing ejb

我有一个具有这种结构的Maven项目:

-myproject
  -myproject-ear
  -myproject-service
    -webservice
  -myproject-ejb
Run Code Online (Sandbox Code Playgroud)

myproject-ejb我有这个java包:

-src/main/java/
-src/test/java/
Run Code Online (Sandbox Code Playgroud)

我有一个EJB和相应的bean实现

-src/main/java/org/mypackage/MyBean.java
-src/main/java/org/mypackage/MyBeanImpl.java
Run Code Online (Sandbox Code Playgroud)

src/test/java/我有一个名为MyBeanTest.java的测试中,代码如下:

import javax.ejb.EJB;
import org.mypackage.MyBean;
import org.junit.*;

public class MyBeanTest {

    @EJB
    private MyBean myBean;

    @Test
    public void testBean() {
        System.out.println("myBean: "+myBean); // prints null
        myBean.writeToDB("Hello", "World"); // fails since myBean is null
    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行单元测试时,myBean为空.我想知道为什么@EJB注释不起作用.测试包与bean在同一个应用程序中,所以@EJB应该工作.

有任何想法吗?

编辑1
我发现此链接与我有相同的问题,但解决方案似乎不适合我.我做错了吗?

package org.myproject.ejb;

import java.util.Hashtable;
import java.util.Properties;

import javax.ejb.EJB;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;

import org.myproject.ejb.MyBean;
import org.jboss.ejb.client.ContextSelector;
import org.jboss.ejb.client.EJBClientConfiguration;
import org.jboss.ejb.client.EJBClientContext;
import org.jboss.ejb.client.PropertiesBasedEJBClientConfiguration;
import org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector;
import org.junit.*;

public class MyBeanTest {

    private MyBean myBean;

    @Before
    public void init() {
        try {
            Properties clientProp = new Properties();
            clientProp.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
            clientProp.put("remote.connections", "default");
            clientProp.put("remote.connection.default.port", "4447");
            clientProp.put("remote.connection.default.host", "localhost");
            clientProp.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");


            EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(clientProp);
            ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc);
            EJBClientContext.setSelector(selector);

            Properties env = new Properties();
            env.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
            env.put(Context.SECURITY_PRINCIPAL, "admin");
            env.put(Context.SECURITY_CREDENTIALS, "testing");
            InitialContext ctx = new InitialContext(env);
            myBean = (MyBean) ctx.lookup("java:app/myproject-ejb-1.0-SNAPSHOT/MyBeanImpl");
        } 
        catch(NamingException ex) {
            ex.printStackTrace();
        }
    }

    @Test
    public void testBean() {
        System.out.println("ejb: "+myBean); // prints null
    }
}
Run Code Online (Sandbox Code Playgroud)

我通过上面的配置得到的错误是:

WARN: Unsupported message received with header 0xffffffff
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:662)
    at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:307)
    at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:344)
Run Code Online (Sandbox Code Playgroud)

Gle*_*est 5

  1. 容器资源注入(例如@EJB)需要填充的JNDI目录,并且只能在Java EE容器中执行的Java EE托管组件中工作.对单元测试来说是一个挑战.请参阅JSR318 Java EE 6平台规范,EE.5资源,命名和注入一节.

  2. 您现在正在尝试JNDI查找 - 远程连接其JNDI上下文的Java SE单元测试应用程序.缺点:必须部署完整的Java EE 6应用程序作为运行测试的前提条件; test-bugfix-build-deploy-retest生命周期可能会减慢速度.

    一些问题:

    • 您的用户名/密码属性与JBoss doc不同;
    • 从doc看来,JNDI查找名称需要"ejb:..."而不是"java:app/..."因为JBoss EJB-client-project代码使用它来拦截查找.同样来自Java EE 6平台规范EE.5.2.2:命名java:app空间中的名称由单个Java EE应用程序中所有模块中的所有组件共享.如果您的测试是一个单独的JSE应用程序使用java:app,我怀疑JBoss将其视为与单个Java EE应用程序分开,并且查找将失败.
    • 确保查找接口,而不是远程访问的实现类(即EJB无接口视图)
    • 您正在引用一个不寻常的参考,显示直接使用EJBClientConfiguration和EJBClientContext.这似乎不是必需/首选.

    尝试以下操作:

  3. 将来:使用CDI注射; @Mock用于"POJO"单元测试的JUnit + CDI ; Arquillian用于容器中的"Java EE"单元/模块测试.然后你可以避免/减少上面的(2)测试(JSE客户端 - > EJB).CDI支持:

    • 将Java EE资源注入POJO(包括@EJB注释).这仍然需要部署的Java EE应用程序/组件和填充的JNDI目录进行查找.
    • 托管bean作为POJO或Java EE组件(包括EJB) - 使用优秀的@Inject注释将"any"注入"any".没有JNDI目录的工作,是类型安全和bean范围感知.

    • 通过简单的模拟支持单元测试.使用@Mock&@Specializes为任何bean声明替换版本.测试没有EJB的EJB客户端.将EJB测试为POJO.

    要启用CDI,请包含一个beans.xml文件(如果通过注释进行所有配置,则可以为空).

    要声明托管bean:

    • 上面的可选范围,例如 @SessionScoped
    • no-arg构造函数/ @Injecton构造函数

    用它来注入一个引用:

     @Inject (optional @MyDeclaredQualifier) private MyBean myBean;
    
    Run Code Online (Sandbox Code Playgroud)

    Arquillian("JUnit for Java EE 6")在Java EE服务器上运行测试代码.它动态地将测试代码部署到已配置的容器并运行测试.它支持@EJB注释,JNDI连接变得简单,您可以在单元测试中包含Java EE类,而无需进行模拟或重构以从中抽象出来.