July 29, 2009

Effective Unit Testing EJB 3.0 with OpenEJB

In my latest project we used EJB 3.0 and Maven2. And as any good programmer I wanted to have good code quality, which means good unit test test coverage, but my expectation on how to achieve this in an efficient manner was quite low, since mine previous experience with EJB 2.1 did not include any good testing experience. Even if coding EJB 3.0 has been greatly simplified, the fact still remains that the enterprise bean has still to be deployed before it can be tested. Of course can one program such that the logic is in a POJO rather directly in the bean itself, but to really be sure it is working the enterprise bean has to be deployed in a container, to be testable. So I started looking at different Maven plugin such as, jboss-maven-plugin and cargo-maven2-plugin, but then came across OpenEJB, which is really the salvation if you are developing EJB 3.0 and want good testing possibility. OpenEJB enable unit testing of enterprise beans as any POJO class! Lets look at some examples.
public class UserServiceSLSBTest {
private static final String EXPECTED_USERNAME_1 = "fornamn.1.efternamn@domain.com";
private static final String EXPECTED_USERNAME_2 = "fornamn.2.efternamn@domain.com";
private static final String EXPECTED_USERNAME_3 = "fornamn.3.efternamn@domain.com";
private Context ctx;

@BeforeClass
public static void oneTimeSetUp() {
}

@AfterClass
public static void oneTimeTearDown() {
}

@Before
public void setUp() throws Exception {
Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");

props.put("blogUnmanaged", "new://Resource?type=DataSource");
props.put("blogUnmanaged.JtaManaged", "false");
props.put("blogUnmanaged.JdbcDriver", "com.mysql.jdbc.Driver");
props.put("blogUnmanaged.JdbcUrl", "jdbc:mysql://localhost:3306/blog");
props.put("blogUnmanaged.username", "root");
props.put("blogUnmanaged.password", "");
// TODO fix properties hibernate.hbm2ddl.auto=create-drop

ctx = new InitialContext(props);
}

@After
public void tearDown() {
}

private UserServiceLocal createUserService() throws Exception {
Object obj = ctx.lookup(UserServiceSLSB.class.getSimpleName() + "Local");

assertNotNull(obj);
assertTrue(obj instanceof UserServiceLocal);
return (UserServiceLocal) obj;
}

@Test
public void testCreateUser() throws Exception {
UserServiceLocal userService = createUserService();

User user = new User();
user.setUsername(EXPECTED_USERNAME_1);
user.setPassword(EXPECTED_USERNAME_1);
user.setRoles(Roles.ADMIN);

userService.storeUser(user);
}
}


And the Stateless Session Bean

@Stateless
public class UserServiceSLSB implements UserServiceLocal {
private static final Logger log = LoggerFactory.getLogger(UserServiceSLSB.class);
@PersistenceContext(name = "blog-ejb", unitName = "blog-ejb")
protected EntityManager em;
@Resource
private SessionContext context;

@Override
public User findUserByUsername(String username) {
log.info("findUserByUsername username='" + username + "'...");
Query q = em.createQuery("SELECT u from User as u where u.username=:username");
q.setParameter("username", username);
List list = q.getResultList();
return (list.size() > 0) ? (User) list.get(0) : null;
}

private void checkExistingUser(User user) throws DuplicateException {
User existingUser = findUserByUsername(user.getUsername());
if (existingUser != null)
throw new DuplicateException("Duplicate " + user);
}

@Override
public void storeUser(User user) throws CredentialException, DuplicateException {
log.info("storeUser " + user + "...");
checkExistingUser(user);
if (user.getUserId() == null)
em.persist(user);
else
em.merge(user);
}
}


And the SLSB local interface.

@Local
public interface UserServiceLocal {

public User findUserByUsername(String username);

public void storeUser(User user) throws CredentialException, DuplicateException;
}


And in the pom.xml simply add the dependency to OpenEJB and you are ready to go.

<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-core</artifactId>
<version>3.1.1</version>
<scope>test</scope>
</dependency>

No comments: