Testing
From Resin 3.0
CanDI/Bean Container Testing Support
CanDI/Bean Container based testing is supported in two forms. The first mechanism is a low level bean container API that can be used in any Java SE environment including a command line application. The second form is a high-level annotation based API to integrate with JUnit 4. In both cases, the bean container scans for and deploys exploded and unexploded modules in the classpath and/or file system.
The simple bootstrap API for Java SE environments is useful for newcomers to Resin as well as people who not use any test frameworks for testing. The following is example code for the Java SE API:
public class AccountServiceTest {
public static void main (String[] arguments) { ResinBeanContainer resin = new ResinBeanContainer();
// Should be possible to add or specify file-system based modules instead of class-path // scanning. This is needed for Arquillian. // resin.addModule("test1.jar", "test2.jar"); // Optional, is overriding default descriptor locations for testing. resin.addBeanXML("beans-test-1.xml, beans-test-2.xml"); resin.addEjbJarXML("ejb-jar-test-1.xml, ejb-jar-test-2.xml"); resin.addPersistenceXML( "persistence-test-1.xml, persistence-test-2.xml");
// Scans and deploys exploded or unexploded jars. resin.start(); // Detect currently running instances.
// Substitute for injection - could also support getting the JNDI context. AccountService accountService = resin.lookup(AccountService.class);
resin.close(); // Optional. } }
In the example above, the capability to programmatically specify XML deployment descriptors enables developers to use test specific descriptors when needed (like test databases, mock objects, etc). By default, the bean container will look for the default locations for descriptors (e.g. META-INF/ejb-jar.xml, WEB-INF/ejb-jar.xml, META-INF/persistence.xml, META-INF/beans.xml etc). The addModule method is useful for cases where the deployed application resides in a location other than the current class-path. It is also required for Arquillian since it uses custom micro-deployment files. The start method starts the bean container if it is not already running. This optimization would be useful while running a suite of tests. Lookup methods are used to get reference to deployed components. In addition, the bean container should support getting the JNDI context directly. The close method shuts-down a bean container instance. IT is optional and only needed in the case where it is necessary to open and close containers in the same process. This essentially mirrors what is needed for the embeddable container API in EJB 3.1 Lite.
The JUnit integration builds on the basic Java SE based functionality as shown below:
@RunWith(ResinBeanContainerRunner.class) @ResinBeanConfiguration(beanXML="beans-test.xml", ejbJar="ejb-jar-test.xml", persistenceXml="persistence-test.xml") public class AccountServiceTest {
@Inject private AccountService accountService;
@Test public void testGetAccount() throws Exception { Account account = accountService.getAccount(1007); assertNotNull(account); } }
The JUnit integration API performs injection into the JUnit test class itself as well as manage the container life-cycle behind the scenes.
Building on the basic functionality, we can also put in integrated support for both EasyMock and DBUnit. For example, we could automatically mock all dependent objects and perform automatic DBUnit setup against any configured database. In the simplest case, we could add support for executing SQL statements against a configured data source before a test is executed. It may also be useful to support automatic transaction roll-back for cases where tests are performed against a non-embedded test database that a test should not change. The examples below illustrate these cases:
@RunWith(ResinBeanContainerRunner.class) @GenerateMockObjects public class AccountServiceTest {
@Inject // All dependencies of the account service are automatically mocked using EasyMock. private AccountService accountService;
@Test public void testGetAccount() throws Exception { Account account = accountService.getAccount(1007); assertNotNull(account); } }
@RunWith(ResinBeanContainerRunner.class) public class AccountServiceTest {
@Inject private AccountService accountService;
@Test @InitializeDataSource(dataSource="jdbc/myDB", sql="script.sql") // Execute some SQL before test // method is invoked. public void testGetAccount() throws Exception { Account account = accountService.getAccount(1007); assertNotNull(account); } }
@RunWith(ResinBeanContainerRunner.class) @TransactionRollback // Rollback transaction. public class AccountServiceTest {
@Inject private AccountService accountService;
@Test // method is invoked. public void testAddAccount() throws Exception { Account account = new Account(); // Setup the account. accountService.addAccount(account); account = accountService.getAccount(1007); assertNotNull(account); } }