Testing

From Resin 3.0

(Difference between revisions)
Jump to: navigation, search
 
(5 intermediate revisions by one user not shown)
Line 1: Line 1:
'''CanDI/Resin Testing Support'''
+
'''CanDI/Bean Container Testing Support'''
  
We can provide very robust support for unit and integration testing via an embedded version of CanDI/Resin as well as specific APIs for JUnit and TestNG. In order to work with the usual usage pattern of JUnit and TestNG, both inside and outside an IDE, the embedded container will have to scan, detect and deploy exploded and unexploded modules in the classpath. We can do this by scanning the existence of annotations as well as deployment descriptors (beans.xml, ejb-jar.xml, application.xml, etc). We'll also need to scan for Resin deployment files in the classpath or in the file system.
+
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.
  
In the most generic case, we should provide a simple bootstrap API for Java SE environments. This would be useful for newcomers to Resin as well as people who not use any test frameworks for testing. A simple use case could be something like this:
+
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 class AccountServiceTest {<br/>
 
   public static void main (String[] arguments) {
 
   public static void main (String[] arguments) {
     EmbeddedResin resin = new EmbedededResin();
+
     ResinBeanContainer resin = new ResinBeanContainer();<br/>
 
+
    // Should be possible to add or specify file-system based modules instead of class-path
     // Optional, is overriding default descriptor locations.
+
    // 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.addBeanXML("beans-test-1.xml, beans-test-2.xml");
 
     resin.addEjbJarXML("ejb-jar-test-1.xml, ejb-jar-test-2.xml");       
 
     resin.addEjbJarXML("ejb-jar-test-1.xml, ejb-jar-test-2.xml");       
 
     resin.addPersistenceXML(
 
     resin.addPersistenceXML(
       "persistence-test-1.xml, persistence-test-2.xml");
+
       "persistence-test-1.xml, persistence-test-2.xml");<br/>
 
+
    // Scans and deploys exploded or unexploded jars.
     resin.start(); // Detect currently running instances.
+
     resin.start(); // Detect currently running instances.<br/>
 
+
    // Substitute for injection - could also support getting the JNDI context.
     AccountService accountService = resin.lookup(AccountService.class);
+
     AccountService accountService = resin.lookup(AccountService.class);<br/>
 
+
 
     resin.close(); // Optional.
 
     resin.close(); // Optional.
 
   }
 
   }
 
  }
 
  }
  
In the example above, we should add the capability to programmatically provide XML deployment descriptors to be used. This will enable developers provide test specific descriptors when needed (like test databases, mock objects, etc). By default, the embedded container can look for the default locations for descriptors in the classpath (e.g. META-INF/ejb-jar.xml, WEB-INF/ejb-jar.xml, META-INF/persistence.xml, etc). The start method should start the embedded container if it is not already running. This optimization would be useful while running a suite of tests. Lookup methods could be used to get reference to deployed components. The close method would shut-down an embedded container instance. It should be 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.
+
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.
  
We can build on the basic Java SE based functionality and provide JUnit and TestNG specific support as below:
+
The JUnit integration builds on the basic Java SE based functionality as shown below:
 
+
@RunWith(ResinJUnitRunner.class)
+
@TestConfiguration(beanXML="beans-test.xml",
+
                    ejbJar="ejb-jar-test.xml", 
+
                    persistenceXml="persistence-test.xml")
+
public class AccountServiveTest {
+
  
 +
@RunWith(ResinBeanContainerRunner.class)
 +
@ResinBeanConfiguration(beanXML="beans-test.xml",
 +
                        ejbJar="ejb-jar-test.xml", 
 +
                        persistenceXml="persistence-test.xml")
 +
public class AccountServiceTest {<br/>
 
   @Inject
 
   @Inject
   private AccountService accountService;
+
   private AccountService accountService;<br/>
 
+
 
   @Test
 
   @Test
 
   public void testGetAccount() throws Exception {
 
   public void testGetAccount() throws Exception {
Line 44: Line 42:
 
  }
 
  }
  
The JUnit/TestNG specific API can perform injection into the JUnit/TestNG class itself as well as manage the container life-cycle behind the scenes. This support mirrors what is provided for Spring testing support.
+
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/JMock and DBUnit. For example, we could automatically mock all dependent objects and perform automatic DBUnit setup against any configured database. The following could also be useful:
+
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:
  
# Support explicitly listing files in the test so that only those files are loaded.
+
@RunWith(ResinBeanContainerRunner.class)
# Excluding files from the test based on regular expressions patterns.
+
@GenerateMockObjects
# Allow selective disabling of Resin functionality for faster startup and shutdown.
+
public class AccountServiceTest {<br/>
# Ant and Maven plugins that mirror the test API (perhaps used for continuous integration).
+
  @Inject // All dependencies of the account service are automatically mocked using EasyMock.
 +
  private AccountService accountService;<br/>
 +
  @Test
 +
  public void testGetAccount() throws Exception {
 +
    Account account = accountService.getAccount(1007);
 +
    assertNotNull(account);
 +
  }
 +
}
  
Seam allows for test scripts written against the web tier using JUnit/TestNG. I don't think we really need this since developers can easily use much better web tier testing tools like Selenium with the basic SE based functionality that is much more popular.
+
@RunWith(ResinBeanContainerRunner.class)
 +
public class AccountServiceTest {<br/>
 +
  @Inject
 +
  private AccountService accountService;<br/>
 +
  @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 {<br/>
 +
  @Inject
 +
  private AccountService accountService;<br/>
 +
  @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);
 +
  }
 +
}

Latest revision as of 23:01, 4 January 2011

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); } }
Personal tools