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.