A great feature of CDI (supported by Java EE starting with version 6)
consist in alternatives. Basically, we want to specify an alternative for
an injected object. Let's have a simple scenario commonly followed in
JSF applications that uses stateless session beans to interact with a database.
Supposing that we write the business logic part to query the database. We can
start with an interface as:
public
interface IFooService {
List getAllFoo();
}
Further, we can write a stateless session bean that implements this
interface and take usage of JPA capabilities to query the database. Well the
things didn't go too far for this part because the database is not ready and
all we can provide is a skeleton like below:
@Stateless
public class
FooService implements IFooService {
@PersistenceContext(unitName =
"fooPU")
private EntityManager em;
@Override
public List getAllFoo() {
// perform
the query
...
return new ArrayList();
}
}
Finally, we inject the stateless session bean in a CDI managed bean as
below:
Named
@RequestScoped
public class
FooBean {
private static final Logger LOG =
Logger.getLogger(FooBean.class.getName());
@Inject
private IFooService fooService;
public void loadAllFoo() {
List allfoo = fooService.getAllFoo();
LOG.log(Level.INFO, "allfoo:{0}",
allfoo);
}
}
Now, let's suppose that we want to run/test this method, but we have an issue. The data that we expect from the database will not be
available, so we think to mock this service and provide a set of dummy data as
below:
@Stateless
public class
FooServiceMock implements IFooService {
@Override
public List getAllFoo() {
return Arrays.asList("foo 1",
"foo 2", "foo 3");
}
}
But, if we test the code now, we will obtain an error of type ambiguous
dependencies. In order to fix this error, we simply annotated our mock with
@Alternative annotation:
@Alternative
@Stateless
public class
FooServiceMock implements IFooService {
@Override
public List getAllFoo() {
return Arrays.asList("foo 1",
"foo 2", "foo 3");
}
}
If we run now, there will be no errors, but the application will not
use the mock. This is happening because our alternative is not activated. We must
accomplish this in the beans.xml,
as below:
<?xml
version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
<alternatives>
<class>beans.FooServiceMock</class>
</alternatives>
</beans>
Done! Now the FooBean
will use the FooServiceMock
instead of FooService. When
the database will be ready/queryable we will simply deactivate the alternative.
Via CDI-Unit we can write a quick test that also uses our mock:
@RunWith(CdiRunner.class)
@ActivatedAlternatives(FooServiceMock.class)
public class
FooBeanTest {
@Inject
FooBean fooBean;
@Test
@InRequestScope
public void testStart() {
fooBean.loadAllFoo();
}
}
The complete example is available here.
Niciun comentariu :
Trimiteți un comentariu