Seam Integration Tests with JUnit
exuisitus Sep 12, 2009 9:41 PMI am evaluating seam for a project. IDE is Eclipse, test framework JUnit.
The discussions about using JUnit in this forum gave some hints but not a
really a solution. So I had do dive into the Seam documentation an cook my
own soup, which I would like to share with the community.
The solution is generic, thus reusable and can be packed in a library.
It consists of two extendible classes which permit to use integration
tests à la TestNG out of the box.
Concept
A sub class of AbstractSeamTest to be in turn subclassed by a test suite (test class) implementation:
TestClass -> [JUnitSeamTest] -> AbstractSeamTest
A class to be subclassed for running multiple test suites (Suite.class runner).
MultipleTestClasses -> [JUnitSeamTestSuiteRunner]
Implementation
JUnitSeamTest.java
import org.jboss.seam.mock.AbstractSeamTest;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
/**
* Superclass for JUnit Seam integration tests.
* A Seam integration test can extend this class for use with JUnit.
*
* One or all of the JUnit life cycle methods can, but must not, be
* overwritten by the extending class. If you do, you m u s t assign the correct
* annotation and call super.xxxx() or the static equivalent
* JUnitSeamTest.xxxx() in the overriding method.
*
* For batch processing, the Suite.class runner class sets the batch flag to true
* in order to prevent multiple start and stops of the embedded container while
* executing the individual test files.
*
* @author Rainer Schön
* @date Sep 09
*/
public class JUnitSeamTest extends AbstractSeamTest {
// static helper instance to call non static methods in
// a static method
private static JUnitSeamTest seamTest = new JUnitSeamTest();
private static boolean batch = false;
/*
* JUnit test life cycle methods.
*/
@BeforeClass
public static void setUpBeforeClass() throws Exception {
if (!batch) seamTest.startContainer();
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
if (!batch) seamTest.stopContainer();
}
@Before
public void setUp() throws Exception {
setupClass();
begin();
}
@After
public void tearDown() throws Exception {
end();
cleanupClass();
}
/*
* Helper methods
*/
// starts the JBoss embedded ejb container
public void startContainer() throws Exception {
super.startSeam();
}
// stops the JBoss embedded ejb container
public void stopContainer() throws Exception {
super.stopSeam();
}
public boolean isBatch() {
return batch;
}
// used by a JUnit.Suite runner to set the batch flag
public void setBatch(boolean fBatch) {
batch = fBatch;
}
}
JUnitSeamTestSuiteRunner.java
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/**
* Superclass for running Seam integration tests in batch mode. This
* class does n o t extend JUnitSeamTest but instead creates an instance
* to start and stop the container at the beginning and the end of the
* processing and to set the batch flag.
*
* The subclass must only assign the @SuiteClasses({x.class, y.class})
* annotation.
*
* @author Rainer Schön
* @Date Sep 09
*/
@RunWith(Suite.class) // JUnit batch runner
public class JUnitSeamTestSuiteRunner {
private static JUnitSeamTest seamTest = new JUnitSeamTest();
@BeforeClass
public static void setUpBeforeClass() throws Exception {
// set the flag to signal batch mode
seamTest.setBatch(true);
seamTest.startContainer();
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
seamTest.stopContainer();
seamTest.setBatch(false);
}
}
Examples
Run single test suite (test class)
import static org.junit.Assert.*;
import java.util.List;
import javax.faces.application.FacesMessage;
import org.jboss.seam.faces.FacesMessages;
import org.junit.Test;
/**
* Working example of a Seam integration test using the JUnit Seam integration
* test port.
*
*/
public class SeamIntegrationTestWithJUnit extends JUnitSeamTest {
@Test
public void testSomething() throws Exception {
new FacesRequest("/some.xhtml") {
@Override
protected void updateModelValues() {
// do some model updates here
// test the results
assertTrue(true);
}
@Override
protected void invokeApplication() {
// Invoke a method and test the outcome
final String outcome = "fantastic";
assertTrue(outcome != null && outcome.equals("fantastic"));
}
@Override
protected void renderResponse() throws Exception {
final String msg = "This JUnit Seam Test port is great!";
// test some results in the render response phase
List<FacesMessage> messages =
FacesMessages.instance().getCurrentGlobalMessages();
messages.add(new FacesMessage(msg));
assertTrue(messages.size() == 1);
assertEquals(FacesMessage.SEVERITY_INFO,
messages.get(0).getSeverity());
assertTrue(messages.get(0).getSummary().
contains(msg));
}
}.run();
}
}
Run multiple test suites (test classes)
import org.junit.runners.Suite.SuiteClasses;
/**
* Example for running multiple Seam integration test classes at once.
*/
@SuiteClasses({
OneTest.class,
TwoTest.class,
ThreeTest.class,
NTest.class})
public class MultipleSeamIntegrationTests extends JUnitSeamTestSuiteRunner{
// empty class, only used to assign @SuiteClasses annotation with
// the array of test classes to run
}
Important
Ensure, that the embedded JBoss container fires up correctly.
If you use Java 6, you must start the VM with the follwing argument:
-Dsun.lang.ClassLoader.allowArraySyntax=true
in Eclipse put this argument in the JUnit launch configuration (box VM) for each
test you want to run (which you will forget most of the time for the first launch!).