When teaching unit testing in the context of a simple EJB3.1 application, I was looking for an easy way of testing managed beans and session beans inside Glassfish. Of course, one can test out-of-container or use an embedded container (but I didn't quite figure out how to do that with Glassfish v3—I'd appreciate hints), or a mock container (but that seemed to require a bit of setup).
I hit upon a scheme that I had not seen elsewhere: put the unit tests in the container and trigger them with a servlet that reports the outcomes. Advantages:
Here is how to do it.
Add the JUnit 4 JAR to WEB-INF/lib.
Add the following servlet to your WAR file:
package myapp.servlets;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.internal.JUnitSystem;
import org.junit.runner.JUnitCore;
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String className = request.getParameter("class");
response.setContentType("text/plain");
OutputStream out = response.getOutputStream();
final PrintStream pout = new PrintStream(out);
new JUnitCore().runMain(new JUnitSystem() {
public PrintStream out() { return pout; }
public void exit(int arg0) {}
}, className);
out.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
Add the following entries to web.xml:
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>myapp.servlets.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
Write your JUnit test case in the usual way. For example, here is a test for a session bean:
package myapp.session;
import static org.junit.Assert.assertEquals;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.junit.Before;
import org.junit.Test;
import myapp.entity.Person;
public class UserSBTest {
private UserSB userSB;
@Before public void init() throws NamingException {
InitialContext context = new InitialContext();
userSB = (UserSB) context.lookup("java:global/MyApp/UserSB");
}
@Test public void testAddUser() {
Person p = new Person();
p.setFirstName("Fred");
p.setUsername("fred");
userSB.save(p);
Person q = userSB.find("fred");
assertEquals(p.getFirstName(), q.getFirstName());
userSB.removePerson("fred");
}
}
Unfortunately, you can't just have the container inject the session bean.
@EJB private UserSB userSB; // Doesn't work when JUnit loads the class
When JUnit loads the class, it doesn't deal with EJB annotations.
Then point your browser to
http://localhost:8080/MyApp/test?class=myapp.session.UserSBTest
Proverbial exercise to the reader: Add a green bar when the test cases pass.