Unit Testing EJB 3.1 ...When 0.8 Seconds Are Too Long [SOURCE CODE INCLUDED]
EJB 3 are just POJOs, so there is no need to unit-test them inside the EJB container. You could just instantiate them in the unit test. Sometimes it is needed to change a behavior of a session bean, to cause an exception or test the boundary in another:
@Stateless
public class ServiceFacade {
@EJB
Service service;
public boolean isOne(){
int nr = service.getNumber();
return (nr == 1);
}
}
I would like to test, whether the ServiceFacade returns "false" in case the number is not 1.The problem:
@Stateless
public class Service {
public int getNumber(){
return 1;
}
}
...its hardcoded....
Mockito is just perfect for testing such cases.
import com.abien.business.nointerface.control.Service;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
public class ServiceFacadeTest {
@Test
public void twoIsFalse(){
Service service = mock(Service.class);
when(service.getNumber()).thenReturn(2);
ServiceFacade facade = new ServiceFacade();
facade.service = service;
assertFalse(facade.isOne());
}
}
You can easily change the return value of every (non-final) class for test purposes. The whole project, with mockito libraries, was pushed into http://kenai.com/projects/javaee-patterns (tested with Netbeans 6.7rc3 and Glassfish v3 Preview).
The whole test is executed in 0.2 seconds on my machine. Four times faster than 0.8 :-). Btw. Glassfish v3 (with EJB 3.1 container) starts in about 5-10 seconds - the whole application is deployed in about 2 seconds...
[In the Real World Patterns - Rethinking Best Practices book several unit-testing strategies are discussed - but not mockito. It will change in the Iteration Two]
geee, public property!
i'm using EasyGloss for this. This thing can inject evrything i need. For mocking i'm using EasyMock...
@Before
...
private JavaEEGloss gloss = new JavaEEGloss();
DateUtilLocal dateUtilBean = EasyMock.createMock(DateUtilLocal.class);
gloss.addEJB(dateUtilBean);
SummaryReportLocal summaryReportBean = gloss.make(SummaryReportBean.class);
@Test
...
expect(dateUtilBean.getDate()).andReturn(new Date());
replay(dateUtilBean);
summaryReportBean.aggregate();
verify(dateUtilBean);
[/code]
Posted by temonix on June 26, 2009 at 11:54 AM CEST #
@Temonix
its not public, but package visibility. But seriously - why not? What can happen in worst case?
Thank you for the EasyMock sample!,
regards,
adam
Posted by Adam Bien on June 26, 2009 at 12:17 PM CEST #
> What can happen in worst case?
There will break the encapsulation. User ServiceFacade shouldn't know that inside there is a variable service. Using private statement prevents the possibility that not experienced developer wants to have access to the service directly
Posted by temonix on June 26, 2009 at 04:08 PM CEST #
@Temonix,
>not experienced developer wants to have access to the service directly
did you ever encounter such a problem in practice? In EJB 3.0 it cannot happen, because of mandatory business interfaces. But you are right - it's potentially possible in EJB 3.1.
Posted by Adam Bien on June 26, 2009 at 06:26 PM CEST #
I tried to do a test like this one but how do I get my beans injected? They are 'null', when I start the test in Eclipse as a JUnit-test.
Posted by Ralf on January 13, 2011 at 05:14 PM CET #
@Ralf
> but how do I get my beans injected?
You don't. Either you mock them and explicitly assign the mocked beans to the @EJB annotated property of the facade (see the last-but-one line of the test method "facade.service = service;") or you want integration testing with some container functionality, which is just not what this post is about.
Posted by Oriza Triznyak on November 15, 2011 at 03:06 PM CET #
I am trying to use your example, but my test fails at "facade.service = service". It looks like my test case doesn't have access to the @EJB Service in the ServiceFacade.
Ecipse is suggesting I use setters and getters for @EJB Service, or change the access to public. However in my implementation I dont want to change the access rights. Is there a way to associate the @EJB Service to the created ServiceFacade other than "facade.service = service"?
Posted by Yasser on December 24, 2013 at 08:23 PM CET #