Testing ejb with stub and openejb framework
I am trying to test an EJB that have another one injected in it. For the tests purpose I want to use a stub for the injected EJB. I had use openEJB as framework for the EJB for the testing.
Here is the EJB :
@Stateless
@Local(IService.class)
public class Service implements IService {
@EJB
private IBean bean;
@Override
public String doService(String data) {
return bean.process(data);
}
}
The real injected EJB :
@Stateless
@Local(IBean.class)
public class Bean implements IBean {
private static Logger logger = Logger.getLogger(Bean.class);
@Override
public String process(String data) {
logger.info("Bean processing : " + data);
return "Bean processing : " + data;
}
}
The stub version of the EJB :
@Stateless
@Local(IBean.class)
public class BeanStub implements IBean {
private static Logger logger = Logger.getLogger(BeanStub.class);
@Override
public String process(String data) {
logger.info("Stub processing : " + data);
return "Stub processing : " + data;
}
}
And the JUnit test used :
public class ServiceTest {
private static Logger logger = Logger.getLogger(ServiceTest.class);
private static InitialContext context;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
// openEJB
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,"org.apache.openejb.client.LocalIn开发者_C百科itialContextFactory");
p.put("openejb.altdd.prefix", "stub"); // use specific ejb-jar
p.put("openejb.descriptors.output", "true");
context = new InitialContext(p);
}
@Test
public void testServiceStub() {
try {
IService service = (IService) context.lookup("ServiceStubLocal");
assertNotNull(service);
String msg = service.doService("service");
assertEquals("Stub processing : service", msg);
} catch (NamingException e) {
logger.error(e);
fail(e.getMessage());
}
}
}
I had try to override the use of the real EJB by the stub one, using a specific ejb-jar (I want to use "BeanStub" instead of default "Bean" in my service) :
<ejb-jar>
<enterprise-beans>
<session id="ServiceStub">
<ejb-name>ServiceStub</ejb-name>
<ejb-class>tests.Service</ejb-class>
<ejb-local-ref>
<ejb-ref-name>tests.Service/bean</ejb-ref-name>
<ejb-link>BeanStub</ejb-link>
</ejb-local-ref>
</session>
</enterprise-beans>
</ejb-jar>
Unfortunatly I have a problem the EJB are declared :
Apache OpenEJB 3.1.4 build: 20101112-03:32 http://openejb.apache.org/ 17:14:29,225 INFO startup:70 - openejb.home = D:\Workspace_Java\tests\testejb 17:14:29,225 INFO startup:70 - openejb.base = D:\Workspace_Java\tests\testejb 17:14:29,350 INFO config:70 - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service) 17:14:29,350 INFO config:70 - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager) 17:14:29,381 INFO config:70 - Found EjbModule in classpath: D:\Workspace_Java\tests\testejb\target\test-classes 17:14:29,412 INFO config:70 - Found EjbModule in classpath: D:\Workspace_Java\tests\testejb\target\classes 17:14:29,428 INFO config:70 - Beginning load: D:\Workspace_Java\tests\testejb\target\test-classes 17:14:29,428 INFO config:70 - AltDD ejb-jar.xml -> file:/D:/Workspace_Java/tests/testejb/target/test-classes/META-INF/stub.ejb-jar.xml 17:14:29,850 INFO config:70 - Beginning load: D:\Workspace_Java\tests\testejb\target\classes 17:14:29,850 INFO config:70 - AltDD ejb-jar.xml -> file:/D:/Workspace_Java/tests/testejb/target/classes/META-INF/stub.ejb-jar.xml 17:14:29,850 INFO config:70 - Configuring enterprise application: classpath.ear 17:14:29,912 INFO config:70 - Configuring Service(id=Default Stateless Container, type=Container, provider-id=Default Stateless Container) 17:14:29,912 INFO config:70 - Auto-creating a container for bean ServiceStub: Container(type=STATELESS, id=Default Stateless Container) 17:14:29,912 INFO options:70 - Using 'openejb.descriptors.output=true' 17:14:29,912 INFO options:70 - Using 'openejb.descriptors.output=true' 17:14:29,928 INFO config:70 - Dumping Generated ejb-jar.xml to: C:\TEMP\ejb-jar-6391test-classes.xml 17:14:29,959 INFO config:70 - Dumping Generated openejb-jar.xml to: C:\TEMP\openejb-jar-6392test-classes.xml 17:14:29,959 INFO options:70 - Using 'openejb.descriptors.output=true' 17:14:29,959 INFO config:70 - Dumping Generated ejb-jar.xml to: C:\TEMP\ejb-jar-6393classes.xml 17:14:29,975 INFO config:70 - Dumping Generated openejb-jar.xml to: C:\TEMP\openejb-jar-6394classes.xml 17:14:30,006 INFO config:70 - Enterprise application "classpath.ear" loaded. 17:14:30,084 INFO startup:70 - Assembling app: classpath.ear 17:14:30,131 INFO startup:70 - Jndi(name=ServiceStubLocal) --> Ejb(deployment-id=ServiceStub) 17:14:30,131 ERROR startup:46 - Jndi name could not be bound; it may be taken by another ejb. Jndi(name=openejb/Deployment/ServiceStub/tests.IService!Local) 17:14:30,131 INFO startup:70 - Undeploying app: classpath.ear 17:14:30,147 ERROR startup:50 - Application could not be deployed: classpath.ear org.apache.openejb.OpenEJBException: Creating application failed: classpath.ear: Unable to bind business local interface for deployment ServiceStub at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:679) at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:450)
Is there something wrong in the approach, or in the way to write the ejb-jar ?
I had similar problems and hurdles with OpenEJB. If you need stubbing and mocking for tests (who doesn't) have a look who I finally managed to handle it (with a great help from David - OpenEJB co-founder). In the latest version (3.1.4) OpenEJB works pretty much like Arquillian, allowing inner-class test driver, without ejb-jar.xml and classpath scanning.
I've described my hurdles here: http://jakub.marchwicki.pl/posts/2011/07/01/testing-ejb-application-openejb-without-classpath-scanning/. Have a look, maybe that will make your testing easier.
Why don't you simply use a mocking framework like EasyMock or Mockito to test this. You wouldn't need any deployment descriptor, EJB container, JNDI lookup, etc. Just this kind of code :
@Test
public void testDoService() {
IBean mockBean = EasyMock.createMock(IBean.class);
mockBean.process("data");
EasyMock.replay(mockBean);
Service serviceToTest = new Service(mockBean);
serviceTotest.doService("data");
EasyMock.verify(mockBean);
}
And it would certainly run much faster, too.
精彩评论