Version 2

    In Part1 of this tutorial we looked at testing pojo beans injected with the built-in scopes. Now we are going to look at unit testing a session bean, and again we are not going to start up a JEE6 container, or even an embedded ejb3.1 lite container. We are going to create a mock EntityManager and inject it into our session bean under test. We need to be clear that this is not an integration test of the session bean. We are not testing transaction handling or database access.

     

    Firstly we need a session bean. Following the model from part1, we are going to create a House bean that stores Room objects, that in turn can tell us if a light is on. Here is the local interface and bean:

     

    @Local
    public interface House {
        public String getLightState(String room);
    }

     

    @Stateless
    public class HouseBean implements House {
        @Inject private EntityManager em;
        public String getLightState(String roomName) {
            Room room = em.find(Room.class, roomName);
            return room.getLightState();
        }
    }

     

    The first question that comes to mind is 'Why @Inject rather than the @PersistenceContext I use all the time?' The answer is simply that the container that manages @PersistenceContext injection doesn't know anything about our mock EntityManager. We need to create a level of indirection so that Weld can step in and help us out. This is provided by a bean producer:

     

    public class BeanProducer {
        @Produces @PersistenceContext(unitName="weld-test") EntityManager em;
    }

     

    Now under normal circumstances the @Inject in our HouseBean is satisfied by the EntityManager "produced" by the @PersistenceContext injection. However we can override that behaviour by specifying an alternate implementation of EntityManager. Here is a snip of that class:

     

    @Alternative
    @ApplicationScoped
    public class MockEntityManager implements EntityManager {
        private Map<Class, Map<Object, Object>> dbStore = new HashMap<Class, Map<Object, Object>>();
        // mock population method
        public void store(Object key, Object instance) {
            Map<Object, Object> typeMap = dbStore.get(instance.getClass());
            if (typeMap == null) {
                typeMap = new HashMap<Object, Object>();
                dbStore.put(instance.getClass(), typeMap);
            }
            typeMap.put(key, instance);
        }
        public <T> T find(Class<T> type, Object key) {
            Map<Object, Object> typeMap = dbStore.get(type);
            if (typeMap == null) {
                return null;
            } else {
                return (T)typeMap.get(key);
            }
        }

     

    You will find this class in the src/test/java directory, because we only need it for testing. To get Weld to use it as an alternative requires two things, the @Alternative annotation and an entry in the beans.xml:

     

    <beans
       xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
       <alternatives>
             <class>test.MockEntityManager</class>
       </alternatives>
    </beans>

     

    This beans.xml is in src/test/resources because we only need it for testing. It tells Weld to look for our MockEntityManager bean and use it as an alternate implementation of EntityManager.

     

    Now we get to our test:

     

    public class RoomTest {
        private BeanManager beanManager;
        @BeforeClass
        public void Setup() {
            StartMain startMain = new StartMain(new String[0]);
            beanManager = startMain.go();
        }
        @Test
        public void test1() throws Exception {
            Bean emBean = (Bean)beanManager.getBeans(MockEntityManager.class).iterator().next();
            CreationalContext emCc = beanManager.createCreationalContext(emBean);
            MockEntityManager em = (MockEntityManager)beanManager.getReference(emBean, MockEntityManager.class, emCc);
            Room room = new Room();
            room.setName("bedroom");
            room.setLightState("bright");
            em.store("bedroom", room);
           
            Bean houseBean = (Bean)beanManager.getBeans(House.class).iterator().next();
            CreationalContext houseCc = beanManager.createCreationalContext(houseBean);
            House house = (House)beanManager.getReference(houseBean, House.class, houseCc);
            Assert.assertEquals(house.getLightState("bedroom"), "bright");
        }
    }

     

    We configure our ApplicationScope MockEntityManager much the same as we configured injected beans in part 1. When we run the test the alternatives element in our beans.xml directs Weld to inject our MockEntityManager into HouseBean in preference to the produced bean coming from a real persistence unit. The packaged artifact contains no such direction and Weld uses the real EntityManager produced in BeanProducer.