I have a Maven project with this structure:
-myproject
-myproject-ear
-myproject-service
-webservice
-myproject-ejb
In the myproject-ejb
I have this java packages:
-src/main/java/
-src/test/java/
I have an EJB and the corresponding bean implementation in
-src/main/java/org/mypackage/MyBean.java
-src/main/java/org/mypackage/MyBeanImpl.java
In src/test/java/
I have a test called MyBeanTest.java with the following code:
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
}
}
When I run the unit test, the myBean
is null. I am wondering why the @EJB
annotation does not work. The test package is in the same application as the bean, so @EJB
should work.
Any ideas?
EDIT 1
I found this link with the same problem as I have, but the solution there doesn´t seem to work for me. Am I doing anything wrong?
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
}
}
The error I get with the above configuration is:
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)
Container resource injection, such as @EJB, requires a populated JNDI directory and only works within Java EE managed components executing in a Java EE container. Is a challenge for unit testing. See JSR318 Java EE 6 Platform Spec, section EE.5 Resources, Naming, and Injection.
You're now attempting JNDI lookup - Java SE unit test app remotely connecting its JNDI Context. Disadvantages: must deploy full Java EE 6 app as precondition to run test; test-bugfix-build-deploy-retest lifecycle can slow things.
Some issues:
"ejb:..."
rather than "java:app/..."
because the JBoss EJB-client-project code uses this to intercept the lookup. Also from Java EE 6 platform spec EE.5.2.2: Names in java:app
namespace are shared by all components in all modules in a single Java EE app. If your test is a separate JSE app using java:app
, I suspect JBoss treats it as separate to the single Java EE application, and lookup will fail.Try these actions:
Include these properties:
clientProp.put("remote.connection.default.username", "admin");
clientProp.put("remote.connection.default.password", "testing");
Change client reference:
java:app/myproject-ejb-1.0-SNAPSHOT/MyBeanImpl to
ejb:<app-ear-name>/<module-jar-name>/<jboss-optional-distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>
E.g. if MyBean is a stateless EJB deployed in myproject-ejb-1.0-SNAPSHOT.jar (without any ear). Then:
ejb:/myproject-ejb-1.0-SNAPSHOT//MyBeanImpl!org.mypackage.MyBean
If it's a stateful EJB, then add "?stateful" to string.
Setup ejb-client.properties
directly (via file or program) and apply directly to JNDI Context
. See https://docs.jboss.org/author/display/AS72/EJB+invocations+from+a+remote+client+using+JNDI and https://docs.jboss.org/author/display/AS72/Scoped+EJB+client+contexts and http://middlewaremagic.com/jboss/?p=1177
In future: use CDI for injection; JUnit + CDI @Mock
for "POJO" unit testing; Arquillian for "Java EE" unit/module testing in containers. Then you could avoid/reduce tests like (2) above (JSE client -> EJB).
CDI supports:
@EJB
annotation). This still requires a deployed Java EE app/component and populated JNDI directory to lookup. Managed beans as POJOs or Java EE components (incl. EJBs) - inject "any" to "any" with superior @Inject annotation. Works without JNDI directory, is typesafe & bean scope-aware.
Supports unit testing via simple mocking. Use @Mock
& @Specializes
to declare replacement version for any bean. Test EJB clients without EJBs. Test EJBs as POJOs.
To enable CDI, include a beans.xml
file (can be empty, if all config via annotation).
To declare a managed bean:
@SessionScoped
@Inject
on constructor Use this to inject a reference:
@Inject (optional @MyDeclaredQualifier) private MyBean myBean;
Arquillian ("JUnit for Java EE 6") runs test code itself on a Java EE server. It dynamically deploys test code to configured container(s) and runs tests. It supports @EJB annotation, JNDI connection becomes simple and you can include Java EE classes in unit tests without mocking, or refactoring to abstract away from them.