© Mike Keith, Merrick Schincariol, Massimo Nardone 2018
Mike Keith, Merrick Schincariol and Massimo NardonePro JPA 2 in Java EE 8https://doi.org/10.1007/978-1-4842-3420-4_15

15. Testing

Mike Keith1 , Merrick Schincariol2 and Massimo Nardone3
(1)
Ottawa, Ontario, Canada
(2)
RR 3, RR 3, Almonte, Ontario, Canada
(3)
Helsinki, Finland
 

One of the major selling points of JPA has been the drive toward better testability. The use of plain Java classes and the ability to use persistence outside of the application server has made enterprise applications much easier to test. This chapter covers unit testing and integration testing with entities, with a mix of modern and traditional test techniques.

Testing Enterprise Applications

Testing is generally accepted as being a good thing, but how exactly should we go about doing it? Almost all enterprise application s are hosted in some kind of server environment, whether it is a servlet container like Apache Tomcat or a full Java EE application server. Once deployed to such an environment, the developer is much more isolated from the application than if he was developing in a Java SE runtime environment. At this point, it can be tested only using the public interface of the application, such as a browser using HTTP, web service, RMI, or a messaging interface.

This presents an issue for developers because to do unit testing we want to be able to focus on the components of an application in isolation. An elaborate sequence of operations through a website may be required to access a single method of a bean that implements a particular business service. For example, to view an Employee record, a test client might have to log in using a username and password, traverse several menu options, execute a search, and then finally access the record. Afterward, the HTML output of the report must be verified to ensure that the operation completed as expected. In some applications, this procedure may be short-circuited by directly accessing the URL that retrieves a particular record. But with more and more information cached in HTTP session state, URLs are beginning to look like random sequences of letters and numbers. Getting direct access to a particular feature of an application may not be easy to achieve.

Java SE clients (so called “fat” clients) that communicate with databases and other resources suffer from the same problem despite their ability to execute the program without the need for an application server. The user interface of a Java SE client may well be a Swing application requiring special tools to drive it in order to do any kind of test automation. The application is still just a black box without any obvious way to get inside.

Numerous attempts have been made to expose the internals of an application to testing while deployed on a server . One of the first was the Cactus1 framework, which allows developers to write tests using JUnit, which are then deployed to the server along with the application and executed via a web interface provided by Cactus. Other frameworks adopted a similar approach using RMI instead of a web interface to control the tests remotely. Currently, a framework called Arquillian2 has started to gain some popularity and uses a related approach. We briefly discuss Arquillian at the end of the chapter.

Although effective, the downside to these approaches is that the application server still has to be up and running before we can attempt any kind of testing. For developers who use test-driven development (TDD) , in which tests are written before code and the full unit test suite is executed after every development iteration (which can be as small as a change to a single method), any kind of interaction with the application server is a bit of a problem. Even for developers who practice a more traditional testing methodology, frequent test execution is hampered by the need to keep the application server running, with a packaging and deployment step before every test run.

Clearly, for developers who want to break a Java EE application into its component parts and test those components in isolation, there is a need for tools that will let them directly execute portions of the application outside of the server environment in which it is normally hosted.

Terminology

Not everyone agrees about exactly what constitutes a unit test or an integration test. In fact, it is quite likely that any survey of a group of developers will yield a wide variety of results, some similar in nature while others venture into completely different areas of testing. Therefore we feel it is important to define our terminology for testing so that you can translate it into whatever terms you are comfortable with.

We see tests falling into the following four categories:
  • Unit tests: Unit tests are written by developers and focus on isolated components of an application. Depending on your approach, this may be a single class or a collection of classes. The only key defining elements in our opinion are that the unit test is not coupled to any server resources (these are typically stubbed out as part of the test process) and executes very quickly. It must be possible to execute an entire suite of unit tests from within an IDE and get the results in a matter of seconds. Unit test execution can be automated and is often configured to happen automatically as part of every merge to a configuration management system.

  • Integration tests: Integration tests are also written by developers and focus on use cases within an application. They are still typically decoupled from the application server, but the difference between a unit test and an integration test is that the integration test makes full use of external resources such as a database. In effect, an integration test takes a component from an application and runs in isolation as if it were still inside the application server. Running the test locally makes it much faster than a test hosted in an application server, but still slower than a unit test. Integration tests are also automated and often run at least daily to ensure that there are no regressions introduced by developers.

  • Functional tests: Functional tests are the black box tests written and automated by quality engineers instead of developers. Quality engineers look at the functional specification for a product and its user interface, and seek to automate tests that can verify product behavior without understanding (or caring) how it is implemented. Functional tests are a critical part of the application development process, but it is unrealistic to execute these tests as part of the day-to-day work done by a developer. Automated execution of these tests often takes place on a different schedule, independent of the regular development process.

  • Acceptance tests : Acceptance tests are customer-driven. These tests, usually conducted manually, are carried out directly by customers or representatives who play the role of the customer. The goal of an acceptance test is to verify that the requirements set out by the customer are fulfilled in the user interface and behavior of the application.

In this chapter, we focus only on unit tests and integration tests. These tests are written by developers for the benefit of developers and constitute what is called white box testing. These tests are written with the full understanding of how the application is implemented and what it will take not only to test the successful path through an application but also to trigger failure scenarios.

Testing Outside the Server

The common element between unit tests and integration tests is that they are executed without the need for an application server. Unfortunately for Java EE developers, this has traditionally been very difficult. Applications developed before the Java EE 5 release are tightly coupled to the application server, often making it difficult and counterproductive to attempt replicating the required container services in a stand-alone environment.

To put this in perspective, let’s look at Enterprise JavaBeans as they existed in EJB 2.1. On paper, testing a session bean class should be little more than a case of instantiating the bean class and invoking the business method. For trivial business methods, this is indeed the case, but things start to go downhill quickly once dependencies get involved. For example, let’s consider a business method that needs to invoke another business method from a different session bean.

Dependency lookup was the only option in EJB 2.1, so if the business method has to access JNDI to obtain a reference to the other session bean, either JNDI must be worked around or the bean class must be refactored so that the lookup code can be replaced with a test-specific version. If the code uses the Service Locator3 pattern, we have a bigger problem because a singleton static method is used to obtain the bean reference. The only solution for testing beans that use Service Locators outside the container is to refactor the bean classes so that the locator logic can be overridden in a test case.

Next we have the problem of the dependent bean itself. The bean class does not implement the business interface, so it cannot simply be instantiated and made available to the bean we are trying to test. Instead, it will have to be subclassed to implement the business interface, and stubs for a number of low-level EJB methods will have to be provided because the business interface in EJB 2.1 actually extends an interface that is implemented internally by the application server.

Even if we get that to work , what happens if we encounter a container-managed entity bean? Not only do we have the same issues with respect to the interfaces involved but the bean class is also abstract, with all the persistent state properties unimplemented. We could implement them, but our test framework would rapidly start to outgrow the application code. We can’t even just run them against the database as we can with JDBC code because so much of the entity bean logic, relationship maintenance, and other persistence operations are available only inside an EJB container.

The dirty secret of many applications written using older versions of Java EE is that there is little to no developer testing at all. Developers write, package, and deploy applications; test them manually through the user interface; and then hope that the quality assurance group can write a functional test that verifies each feature. It’s just too much work to test individual components outside of the application server.

This is where EJB, CDI, and JPA come in. In later versions of EJB, a session bean class is a simple Java class for local beans. No special EJB interfaces need to be extended or implemented. Likewise with CDI beans. To unit test the logic in a session or CDI bean, we can often just instantiate it and execute it. If the bean depends on another bean, we can instantiate that bean and manually inject it into the bean being tested. If you are testing code that uses the entity manager and want to verify that it is interacting with the database the way you expect it to, just bootstrap the entity manager in Java SE and make full use of the entity manager outside of the application server.

In this chapter, we demonstrate how to take a bean and JPA code from a Java EE application and run it outside the container using unit testing and integration testing approaches. It is much easier than it was in the past.

JUnit

The JUnit test framework is a de facto standard for testing Java applications. JUnit is a simple unit testing framework that allows tests to be written as Java classes. These Java classes are then bundled together and run in suites using a test runner that is itself a simple Java class. Out of this simple design a whole community has emerged to provide extensions to JUnit and integrate it into all major development environments.

Despite its name, unit testing is only one of the many things that JUnit can be used for. It has been extended to support testing of websites, automatic stubbing of interfaces for testing, concurrency testing, and performance testing. Many quality assurance groups now use JUnit as part of the automation mechanism to run whole suites of end-to-end functional tests.

For our purposes, we look at JUnit in the context of its unit testing roots, and also at strategies that allow it to be used as an effective integration test framework. Collectively we look at these two approaches simply as developer tests because they are written by developers to assist with the overall quality and development of an application.

We assume that you are familiar with JUnit 4 (which makes use of annotations) at this point. Introductory articles and tutorials can be found on the JUnit website at www.junit.org . Many books and other online resources cover testing with JUnit in extensive detail.

Unit Testing

It might seem counterintuitive at first, but one of the most interesting things about entities is that they can participate in tests without requiring a running application server or live database. In the following sections, we look at testing entity classes directly and using entities as part of tests for Java EE components. We also discuss how to leverage dependency injection in unit tests and how to deal with the presence of JPA interfaces.

Testing Entities

Entities are unlikely to be extensively tested in isolation. Most methods on entities are simple getters or setters that relate to the persistent state of the entity or to its relationships. Business methods may also appear on entities, but are less common. In many applications, entities are little more than basic JavaBeans.

As a rule, property methods do not generally require explicit tests. Verifying that a setter assigns a value to a field and the corresponding getter retrieves the same value is not testing the application so much as the compiler. Unless there is a side effect in one or both of the methods, getters and setters are too simple to break and therefore too simple to warrant testing.

The key things to look for in determining whether or not an entity warrants individual testing are side effects from a getter or setter method (such as data transformation or validation rules) and the presence of business methods. The entity shown in Listing 15-1 contains nontrivial logic that warrants specific testing.

@Entity
public class Department {
    @Id private String id;
    private String name;
    @OneToMany(mappedBy="department")
    private Collection<Employee> employees;
    public String getId() { return id; }
    public void setId(String id) {
        if (id.length() != 4) {
            throw new IllegalArgumentException(
                "Department identifiers must be four characters in length");
        }
        this.id = id.toUpperCase();
    }
    // ...
}
Listing 15-1

An Entity That Validates and Transforms Data

The setId() method both validates the format of the department identifier and transforms the string to uppercase. This type of logic and the fact that setting the identifier can actually cause an exception to be thrown suggests that tests would be worthwhile. Testing this behavior is simply a matter of instantiating the entity and invoking the setter with different values. Listing 15-2 shows one possible set of tests.

public class DepartmentTest {
    @Test
    public void testValidDepartmentId() throws Exception {
        Department dept = new Department();
        dept.setId("NA65");
        Assert.assertEquals("NA65", dept.getId());
    }
    @Test
    public void testDepartmentIdInvalidLength() throws Exception {
        Department dept = new Department();
        try {
            dept.setId("NA6");
            Assert.fail("Department identifiers must be four characters");
        } catch (IllegalArgumentException e) {
        }
    }
    @Test
    public void testDepartmentIdCase() throws Exception {
        Department dept = new Department();
        dept.setId("na65");
        Assert.assertEquals("NA65", dept.getId());
    }
}
Listing 15-2

Testing a Setter Method for Side Effects

Testing Entities in Components

The most likely test candidate for entities is not the entity but the application code that uses the entity as part of its business logic. For most applications this means testing session beans, managed beans CDI beans, Spring beans, or whatever flavor of enterprise component you are using. If the entities are obtained or initialized outside the scope of the component, testing is made easy in the sense that the entity class can simply be instantiated, populated with entity data, and set into the bean class for testing. When used as a domain object in application code, an entity is no different from any other Java class. You can effectively pretend that it’s not an entity at all.

Of course, there is more to unit testing a bean than simply instantiating entities to be used with a business method. We also need to be concerned with the dependencies that the bean has in order to implement its business logic. These dependencies are usually manifested as fields on the bean class that are populated using a form of dependency injection (or in some cases dependency lookup).

Injection includes the following Testing Enrichment:
  • @Resource (reference to any JNDI entry)

  • @EJB (Injects EJB, local/remote)

  • @Inject (CDI beans)

  • @PersistenceContext (JPA persistence context)

When writing unit tests, the goal is to introduce the minimum set of dependencies required to implement a particular test. If you are testing a business method that needs to invoke a method on a separate interface, you should worry only about providing a stubbed version of the interface. If the bean uses a data source but is not relevant to your testing, then ideally you want to ignore it entirely.

Dependency injection is the key to effective unit testing. By removing such things as the JNDI API from bean code and eliminating the need for the Service Locator pattern, you can ensure that the bean class has a few dependencies on the container. You need only instantiate the bean instance and manually inject the required resources, the majority of which will be either other beans from the application or test-specific implementations of a standard interface.

As we explained in Chapter 3, the setter injection form of dependency injection is the easiest to use in unit tests. Because the setter methods are almost always public, they can be invoked directly by the test case to assign a dependency to the bean class. Field injection is still easy to deal with, so long as the field uses package scope because the convention for unit tests is to use the same package name as the class that is being tested.

When the dependency is another bean, you must make a choice about whether all the dependencies of the required bean class must be met or whether a test-specific version of the bean should be used instead. If the business method from the dependent bean does not affect the outcome of the test, it may not be worth the effort to establish the full dependency. As an example, consider the bean shown in Listing 15-3. We have shown a single method for calculating years of service for an employee that retrieves an Employee instance using the EmployeeService session bean.

@Stateless
public class VacationBean {
    public static final long MILLIS_PER_YEAR = 1000 * 60 * 60 * 24 * 365;
    @EJB EmployeeService empService;
    public int getYearsOfService(int empId) {
        Employee emp = empService.findEmployee(empId);
        long current = System.currentTimeMillis();
        long start = emp.getStartDate().getTime();
        return (int)((current - start) / MILLIS_PER_YEAR);
    }
    // ...
}
Listing 15-3

Using the EmployeeService Bean in a Different Business Method

Because the only thing necessary to verify the getYearsOfService() method is a single Employee instance with a start date value, there might not be a need to use the real EmployeeService bean, particularly if it has external dependencies of its own that might make it hard to instantiate. A simple subclass of the EmployeeService class that returns an entity instance preconfigured for the test is more than sufficient. In fact, the ability to specify a well-known return value from the findEmployee() method makes the overall test much easier to implement. Listing 15-4 demonstrates using a test-specific subclass implementation of a bean. The implementation is defined as an anonymous inner class in the test class. Creating an implementation specifically for a test is called mocking the class, and the instantiated instance is referred to as a mock object.

public class VacationBeanTest {
    @Test
    public void testYearsOfService() throws Exception {
        VacationBean bean = new VacationBean();
        bean.empService = new EmployeeService() {
            public Employee findEmployee(int id) {
                Employee emp = new Employee();
                emp.setStartDate(new Time(System.currentTimeMillis() -
                                          VacationBean.MILLIS_PER_YEAR * 5));
                return emp;
            }
            // ...
        };
        int yearsOfService = bean.getYearsOfService(0);
        Assert.assertEquals(5, yearsOfService);
    }
    // ...
}
Listing 15-4

Creating a Test-Specific Implementation of a Bean

The Entity Manager in Unit Tests

The EntityManager and Query interfaces present a challenge to developers writing unit tests. Code that interacts with the entity manager can vary from the simple (persisting an object) to the complex (issuing a JP QL query and obtaining the results). There are two basic approaches to dealing with the presence of standard interfaces :
  • Introduce a subclass that replaces methods containing entity manager or query operations with test-specific versions that do not interact with JPA.

  • Provide custom implementations of standard interfaces that may be predictably used for testing.

Before covering these strategies in detail, consider the session bean implementation shown in Listing 15-5 that provides a simple authentication service. For such a simple class, it is surprisingly challenging to unit test. The entity manager operations are embedded directly within the authenticate() method , coupling the implementation to JPA.

@Stateless
public class UserService {
    @PersistenceContext(unitName="EmployeeService")
    EntityManager em;
    public User authenticate(String userId, String password) {
        User user = em.find(User.class, userId);
        if (user != null) {
            if (password.equals(user.getPassword())) {
                return user;
            }
        }
        return null;
    }
}
Listing 15-5

Session Bean that Performs Basic Authentication

The first technique we demonstrate to make this class testable is to introduce a subclass that eliminates entity manager calls. For the UserServiceBean example shown in Listing 15-5, entity manager access must first be isolated to a separate method before it can be tested. Listing 15-6 demonstrates such a refactoring.

@Stateless
public class UserService {
    @PersistenceContext(unitName="EmployeeService")
    EntityManager em;
    public User authenticate(String userId, String password) {
        User user = findUser(userId);
        // ...
    }
    User findUser(String userId) {
        return em.find(User.class, userId);
    }
}
Listing 15-6

Isolating Entity Manager Operations for Testing

With this refactoring complete, the authenticate() method no longer has any direct dependency on the entity manager. The UserService class can now be subclassed for testing, replacing the findUser() method with a test-specific version that returns a well-known result. Listing 15-7 demonstrates a complete test case using this technique.

public class UserServiceTest {
    static final String USER_ID = "test_id";
    static final String PASSWORD = "test_password";
    static final String INVALID_USER_ID = "test_user";
    @Test
    public void testAuthenticateValidUser() throws Exception {
        MockUserService service = new MockUserService();
        User user = service.authenticate(USER_ID, PASSWORD);
        Assert.assertNotNull(user);
        Assert.assertEquals(USER_ID, user.getName());
        Assert.assertEquals(PASSWORD, user.getPassword());
    }
    @Test
    public void testAuthenticateInvalidUser() throws Exception {
        MockUserService service = new MockUserService();
        User user = service.authenticate(INVALID_USER_ID, PASSWORD);
        Assert.assertNull(user);
    }
    class MockUserService extends UserService {
        private User user;
        public MockUserService() {
            user = new User();
            user.setName(USER_ID);
            user.setPassword(PASSWORD);
        }
        User findUser(String userId) {
            if (userId.equals(user.getName())) {
                return user;
            }
            return null;
        }
    }
}
Listing 15-7

Using a Subclass to Eliminate Entity Manager Dependencies

This test case has the advantage of leaving the original authenticate() method implementation intact, only overriding the findUser() method for the test. This works well for classes that have been refactored to isolate persistence operations, but these changes cannot always be made. The alternative is to mock the EntityManager interface. Listing 15-8 demonstrates this approach.

public class UserServiceTest2 {
    static final String USER_ID = "test_id";
    static final String PASSWORD = "test_password";
    static final String INVALID_USER_ID = "test_user";
    @Test
    public void testAuthenticateValidUser() throws Exception {
        UserService service = new UserService();
        service.em = new TestEntityManager(USER_ID, PASSWORD);
        User user = service.authenticate(USER_ID, PASSWORD);
        Assert.assertNotNull(user);
        Assert.assertEquals(USER_ID, user.getName());
        Assert.assertEquals(PASSWORD, user.getPassword());
    }
    @Test
    public void testAuthenticateInvalidUser() throws Exception {
        UserService service = new UserService();
        service.em = new TestEntityManager(USER_ID, PASSWORD);
        User user = service.authenticate(INVALID_USER_ID, PASSWORD);
        Assert.assertNull(user);
    }
    class TestEntityManager extends MockEntityManager {
        private User user;
        public TestEntityManager(String user, String password) {
            this.user = new User();
            this.user.setName(user);
            this.user.setPassword(password);
        }
        public <T> T find(Class<T> entityClass, Object pk) {
            if (entityClass == User.class && ((String)pk).equals(user.getName())) {
                return (T) user;
            }
            return null;
        }
    }
}
Listing 15-8

Using a Mock Entity Manager in a Unit Test

The advantage of this approach over subclassing is that it leaves the original bean class unchanged while allowing it to be unit tested. The MockEntityManager class referenced in the test is a concrete implementation of the EntityManager interface with empty method definitions. All methods that return a value return null or an equivalent instead. By defining it separately, it can be reused for other test cases. Many unit test suites contain a small set of mocked interfaces that can be reused across multiple tests.

Tip

Check out www.mockobjects.com for further information on mock object techniques and open source tools to assist with mock object creation.

Integration Testing

Integration test ing, for our purposes, is an extension of unit testing that takes components of a Java EE application and executes them outside of an application server. Unlike unit testing, in which we went to great lengths to avoid the entity manager, in integration testing we embrace it and leverage the fact that it can be used in Java SE.

The following sections explore using JPA outside of an application server in order to test application logic with a live database, but without starting the application server. To better approximate the runtime environment, the same provider should be used for testing as is used in production.

Using the Entity Manager

In Listing 15-5, we demonstrated a bean that performed basic authentication against a User object retrieved from the database. To unit test this class, a number of techniques were presented to replace or mock the entity manager operation. The downside to this approach is that the test code required to work around external dependencies in the application code can quickly reach a point where it is difficult to maintain and is a potential source of bugs.

Instead of mocking the entity manager, a resource-local, application-managed entity manager may be used to perform tests against a live database. Listing 15-9 demonstrates a functional test version of the UserService test cases.

public class UserServiceTest3 {
    static final String USER_ID = "test_id";
    static final String PASSWORD = "test_password";
    static final String INVALID_USER_ID = "test_user";
    private EntityManagerFactory emf;
    private EntityManager em;
    @Before
    public void setUp() {
        emf = Persistence.createEntityManagerFactory("hr");
        em = emf.createEntityManager();
        createTestData();
    }
    @After
    public void tearDown() {
        if (em != null) {
            removeTestData();
            em.close();
        }
        if (emf != null) {
            emf.close();
        }
    }
    private void createTestData() {
        User user = new User();
        user.setName(USER_ID);
        user.setPassword(PASSWORD);
        em.getTransaction().begin();
        em.persist(user);
        em.getTransaction().commit();
    }
    private void removeTestData() {
        em.getTransaction().begin();
        User user = em.find(User.class, USER_ID);
        if (user != null) {
            em.remove(user);
        }
        em.getTransaction().commit();
    }
    @Test
    public void testAuthenticateValidUser() throws Exception {
        UserService service = new UserService();
        service.em = em;
        User user = service.authenticate(USER_ID, PASSWORD);
        Assert.assertNotNull(user);
        Assert.assertEquals(USER_ID, user.getName());
        Assert.assertEquals(PASSWORD, user.getPassword());
    }
    @Test
    public void testAuthenticateInvalidUser() throws Exception {
        UserService service = new UserService();
        service.em = em;
        User user = service.authenticate(INVALID_USER_ID, PASSWORD);
        Assert.assertNull(user);
    }
}
Listing 15-9

Integration Test for UserService Bean

This test case uses the fixture methods setUp() and tearDown() to create EntityManagerFactory and EntityManager instances using the Java SE bootstrap API and then closes them when the test completes. The test case also uses these methods to seed the database with test data and remove it when the test completes. The tearDown() method is guaranteed to be called even if a test fails due to an exception. Like any JPA application in the Java SE environment, a persistence.xml file will need to be on the classpath in order for the Persistence class to bootstrap an entity manager factory. The file must contain the JDBC connection properties to connect to the database, and if the managed classes were not already listed, class elements would also need to be added for each managed class. If the transaction type was not specified, it will be defaulted to the correct transaction type according to the environment; otherwise, it should be set to RESOURCE_LOCAL. This example demonstrates the basic pattern for all integration tests that use an entity manager.

The advantage of this style of test versus a unit test is that no effort was required to mock up persistence interfaces. Emulating the entity manager and query engine in order to test code that interacts directly with these interfaces suffers from diminishing returns as more and more effort is put into preparing a test environment instead of writing tests. In the worst-case scenario, incorrect test results occur because of bugs in the test harness, not in the application code. Given the ease with which JPA can be used outside the application server, this type of effort may be better spent establishing a simple database test environment and writing automated functional tests.

However, despite the opportunity that testing outside the application server presents, care must be taken to ensure that such testing truly adds value. Quite often, developers fall into the trap of writing tests that do little more than test vendor functionality as opposed to true application logic. An example of this mistake is seeding a database, executing a query, and verifying that the desired results are returned. It sounds valid at first, but all that it tests is the developer’s understanding of how to write a query. Unless there is a bug in the database or the persistence provider, the test will never fail. A more valid variation of this test is to start the scenario farther up the application stack by executing a business method on a session façade that initiates a query and then validating that the resulting transfer objects are formed correctly for later presentation by a JSP page.

Test Setup and Teardown

Many tests involving persistence require some kind of test data in the database before the test can be executed. If the business operation does not create and verify the result of a persistence operation, the database must already contain data that can be read and used by the test. Because tests should ideally be able to set and reset their own test data before and after each test, we must have a way to seed the database appropriately.

This sounds pretty straightforward; use JDBC to seed the database during setUp() and again during tearDown() to reset it. But there is a danger here. Most persistence providers employ some kind of data or object caching. Any time data changes in the database without the persistence provider knowing about it, its cache will get out of sync with the database. In the worst-case scenario, this could cause entity manager operations to return entities that have since been removed or that have stale data.

It’s worth reiterating that this is not a problem with the persistence provider. Caching is a good thing and the reason why JPA solutions often significantly outperform direct JDBC access in read-mostly applications. The Reference Implementation, for example, uses a sophisticated shared-cache mechanism that is scoped to the entire persistence unit. When operations are completed in a particular persistence context, the results are merged back into the shared cache so that they can be used by other persistence contexts. This happens whether the entity manager and persistence context are created in Java SE or Java EE. Therefore, you can’t assume that closing an entity manager clears test data from the cache.

There are several approaches we can use to keep the cache consistent with our test database. The first, and easiest, is to create and remove test data using the entity manager. Any entity persisted or removed using the entity manager will always be kept consistent with the cache. For small datasets, this is very easy to accomplish. This is the approach we used in Listing 15-9.

For larger datasets, however, it can be cumbersome to create and manage test data using entities. JUnit extensions such as DbUnit 4 allow seed data to be defined in XML files and then loaded in bulk to the database before each test begins. So given that the persistence provider won’t know about this data, how can we still use of it? The first strategy is to establish a set of test data that is read-only . As long as the data is never changed, it doesn’t matter whether the entity exists in the provider cache or not. The second strategy is to either use special datasets for operations that need to modify test data without creating it or to ensure that these changes are never permanently committed. If the transaction to update the database is rolled back, the database and cache state will both remain consistent.

Another option is to use the javax.persistence.sql-load-script-source property described in Chapter 14. Creating a script and letting the provider execute it on startup is a simple way to pre-load the database. However, because it is a persistence unit level property, it will only occur once at persistence unit startup time, making it less practical to do on a test-by-test basis.

The last point to consider is explicit cache invalidation. Prior to JPA 2.0, access to the second-level cache was vendor-specific. As discussed in Chapter 12, we can now use the Cache interface to explicitly clear the second-level cache between tests. The following method demonstrates how to invalidate the entire second-level cache given any EntityManagerFactory instance:

public static void clearCache(EntityManagerFactory emf) {
    emf.getCache().evictAll();
}

If there are any open entity managers, the clear() operation on each should be invoked as well. As we discussed before, the persistence context is a localized set of transactional changes. It uses data from the shared cache but is actually a separate and distinct data structure.

Switching Configurations for Testing

One of the advantages of JPA is that metadata specified in annotation form may be overridden or replaced by metadata specified in XML form. This affords us a unique opportunity to develop an application targeting the production database platform and then provide an alternate set of mappings (even query definitions) targeted to a test environment. While this is a common practice and has its benefits, it’s worth noting that if you are running on a test database with alternate mappings and query definitions, there will clearly be at least some differences between the test installation and running in production. Production testing is always going to be necessary, but testing earlier in the cycle on a test database can be done on a more convenient or more accessible database platform and can catch some bugs earlier in the cycle.

In the context of testing, the Java SE bootstrap mechanism will use the persistence.xml file located in the META-INF directory on the classpath. As long as the persistence unit definition inside this file has the same name as the one the application was written to, the test version can retarget it as necessary to suit the needs of the integration test.

There are two main uses for this approach. The first is to specify properties in the persistence.xml file that are specific to testing. For many developers, this will mean providing JDBC connection information to a local database so that tests do not collide with other developers on a shared database.

The second major use of a custom persistence.xml file is to customize the database mappings for deployment on a completely different database platform. For example, if Oracle is your production database and you don’t want to run the full database on your local machine, you can adjust the mapping information to target an embedded database such as Apache Derby.

Note

At the risk of sounding somewhat biased, might we humbly suggest Oracle XE. It represents the power of the Oracle database conveniently sized to an individual machine at no cost. Many of the examples in this book (including the advanced SQL query examples) were developed on Oracle XE.

As an example of when this would be necessary, consider an application that uses the native sequencing of the Oracle database. Derby does not have an equivalent, so table generators must be used instead. First, let’s consider an example entity that uses a native sequence generator:

@Entity
public class Phone {
    @SequenceGenerator(name="Phone_Gen", sequenceName="PHONE_SEQ")
    @Id @GeneratedValue(generator="Phone_Gen")
    private int id;
    // ...
}

The first step to get this entity working on Derby is to create an XML mapping file that overrides the definition of the Phone_Gen generator to use a table generator. The following fragment of a mapping file demonstrates how to replace the sequence generator with a table generator:

<entity-mappings>
    ...
    <table-generator name="Phone_Gen", table="ID_GEN",
                     pk-column-value="PhoneId">
    ...
</entity-mappings>

This is the same technique we applied in Chapter 13 when we discussed overriding a sequence generator.

Finally, we need to create a new persistence.xml file that references this mapping file. If the overrides were placed in a mapping file called derby-overrides.xml, the following persistence unit configuration would apply the mapping overrides:

<persistence>
    <persistence-unit name="hr">
        ...
        <mapping-file>derby-overrides.xml</mapping-file>
        ...
    </persistence-unit>
</persistence>

Unlike the mapping file, which sparsely defines overrides, all the information that was present in the production persistence.xml file must be copied into the test-specific version. The only exception to this is the JDBC connection properties, which will now have to be customized for the embedded Derby instance.

Minimizing Database Connections

Integration tests execute slower than unit tests due to the nature of the database interaction, but what might not be obvious from the test case shown in Listing 15-9 is that two separate connections are made to the database, one each for the testAuthenticateValidUser() and testAuthenticateInvalidUser() tests. JUnit actually instantiates a new instance of the test case class each time it runs a test method, running setUp() and tearDown() each time as well. The reason for this behavior is to minimize the chance of data stored in fields from one test case interfering with the execution of another.

While this works well for unit tests, it may lead to unacceptable performance for integration tests. To work around this limitation, the @BeforeClass and @AfterClass features of JUnit 4 may be used to create fixtures that run only once for all of the tests in a class. Listing 15-10 demonstrates a test suite class that uses this feature at the level of the entire test suite.

@RunWith(Suite.class)
@Suite.SuiteClasses({UserServiceTest3.class})
public class DatabaseTest {
    public static EntityManagerFactory emf;
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        emf = Persistence.createEntityManagerFactory("hr");
    }
    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        if (emf != null) { emf.close(); }
    }}
Listing 15-10

One-Time Database Setup for Integration Tests

Using this test suite as a starting point, all test cases added to the @Suite.SuiteClasses annotation can have access to the correctly populated EntityManagerFactory static field on the DatabaseTest class. The setUp() method of each test case now only needs to reference this class to obtain the factory instead of creating it each time. The following example demonstrates the change required for the UnitServiceTest3 test case:

@Before
public void setUp() {
    emf = DatabaseTest.emf;
    em = emf.createEntityManager();
    createTestData();
}

This is a useful technique to minimize the cost of acquiring expensive resources, but care must be taken to ensure that side effects from one test do not accidentally interfere with the execution of other tests. Because all tests share the same entity manager factory, data may be cached or settings may be changed (supported by some entity manager factories) that have an unexpected impact later on. Just as it is necessary to keep the database tables clean between tests, any changes to the entity manager factory must be reverted when the test ends, regardless of whether the outcome is a success or a failure. It is usually a good idea to clear the cache, as we showed in the “Test Setup and Teardown” section.

Components and Persistence

More often than not, beans in an integration test are no different from beans in a unit test. You instantiate the bean, supply any necessary dependencies, and execute the test. Where we start to diverge is when we take into account issues such as transaction management and multiple bean instances collaborating together to implement a single use case. In the following sections, we discuss techniques to handle more complex bean scenarios when testing outside of the container.

Transaction Management

Transactions lie at the heart of every enterprise application. We made this statement back in Chapter 3 and drove it home in Chapter 6, demonstrating all the different ways in which entity managers and persistence contexts can intersect with different transaction models. It might come as a surprise, then, to learn that when it comes to writing integration tests, we can often sidestep the stringent transactional requirements of the application to easily develop tests outside the container.

The following sections delve into when transactions are really required and how to translate the container-managed and bean-managed transaction models of the Java EE Server into your test environment.

When to Use Transactions

Except for resource-local, application-managed entity managers, which are less frequently used in the Java EE environment, transaction management is the purview of session beans and other components that use JPA. We focus specifically on session beans, but the topics we cover apply equally to transactional persistence operations hosted by any other transaction-capable components.

The transaction demarcation for a session bean method needs to be considered carefully when writing tests. Despite the default assumption that transactions are used everywhere in the application server, only a select number of methods actually require transaction management for the purpose of testing. Because we are focused on testing persistence, the situation we are concerned with is when the entity manager is being used to persist, merge, or remove entity instances. We also need to determine whether these entities actually need to be persisted to the database.

In a test environment, we are using resource-local, application-managed entity managers. Recall from Chapter 6 that an application-managed entity manager can perform all its operations without an active transaction. In effect, invoking persist() queues up the entity to be persisted the next time a transaction starts and is committed. Furthermore, we know that once an entity is managed, it can typically be located using the find() operation without the need to go to the database. Given these facts, we generally need a transacted entity manager only if the business method creates or modifies entities, and executes a query that should include the results.

Although not required to satisfy business logic, a transaction may also be required if you want the results of the operation to be persisted so that they can be analyzed using something other than the active entity manager. For example, the results of the operation can be read from the database using JDBC and compared to a known value using a test tool.

The main thing we want to stress here—before we look into how to implement transactions for session bean tests—is that more often than not, you don’t really need them at all. Look at the sequence of operations you are testing and consider whether the outcome will be affected one way or the other—first if the data must be written to the database, and later if it truly must be committed as part of the test. Given the complexity that manual transaction management can sometimes require, use transactions only when they are necessary.

Container-Managed Transactions

One of the most important benefits of container-managed transactions is that they are configured for bean methods entirely using metadata. There is no programming interface invoked by the bean to control the transaction other than on contextual objects5, and even this occurs only in certain circumstances. Therefore, once we decide that a particular bean method requires a transaction to be active, we need only start a transaction at the start of the test and commit or roll back the results when the test ends.

Listing 15-11 shows a bean method that will require an open transaction during a test. The assignEmployeeToDepartment() method assigns an employee to a given department and then returns the list of employees currently assigned to the department by executing a query. Because the data modification and query occur in the same transaction, our test case will also require a transaction.

@Stateless
public class DepartmentServiceBean implements DepartmentService {
    private static final String QUERY =
        "SELECT e " +
        "FROM Employee e " +
        "WHERE e.department = ?1 ORDER BY e.name";
    @PersistenceContext
    EntityManager em;
    public List assignEmployeeToDepartment(int deptId, int empId) {
        Department dept = em.find(Department.class, deptId);
        Employee emp = em.find(Employee.class, empId);
        dept.getEmployees().add(emp);
        emp.setDepartment(dept);
        return em.createQuery(QUERY)
                 .setParameter(1, dept)
                 .getResultList();
    }
    // ...
}
Listing 15-11

Business Method Requiring a Transaction

Because we are using a resource-local entity manager, we will be simulating container-managed transactions with EntityTransaction transactions managed by the test case. Listing 15-12 shows the test case for the assignEmployeeToDepartment() method. We have followed the same template as in Listing 15-9, so the setUp() and tearDown() methods are not shown. Before the session bean method is invoked, we create a new transaction. When the test is complete, we roll back the changes because it isn’t necessary to persist them in the database.

public class DepartmentServiceBeanTest {
    // ...
    private void createTestData() {
        Employee emp = new Employee(500, "Scott");
        em.persist(emp);
        emp = new Employee(600, "John");
        em.persist(emp);
        Department dept = new Department(700, "TEST");
        dept.getEmployees().add(emp);
        emp.setDepartment(dept);
        em.persist(dept);
    }
    @Test
    public void testAssignEmployeeToDepartment() throws Exception {
        DepartmentServiceBean bean = new DepartmentServiceBean();
        bean.em = em;
        em.getTransaction().begin();
        List result = bean.assignEmployeeToDepartment(700, 500);
        em.getTransaction().rollback();
        Assert.assertEquals(2, result.size());
        Assert.assertEquals("John", ((Employee)result.get(0)).getName());
        Assert.assertEquals("Scott", ((Employee)result.get(1)).getName());
    }
    // ...
}
Listing 15-12

Testing a Business Method That Requires a Transaction

Bean-Managed Transactions

For a bean that uses bean-managed transactions, the key issue we need to contend with is the UserTransaction interface. It may or may not be present in any given bean method and can be used for a number of purposes, from checking the transaction status to marking the current transaction for rollback, to committing and rolling back transactions. Fortunately, almost all the UserTransaction methods have a direct correlation to one of the EntityTransaction methods. Because our test strategy involves a single entity manager instance for a test, we need to adapt its EntityTransaction implementation to the UserTransaction interface.

Listing 15-13 shows an implementation of the UserTransaction interface that delegates to the EntityTransaction interface of an EntityManager instance. Exception handling has been added to convert the unchecked exceptions thrown by EntityTransaction operations into the checked exceptions that clients of the UserTransaction interface will be expecting.

public class EntityUserTransaction implements UserTransaction {
    private EntityManager em;
    public EntityUserTransaction(EntityManager em) {
        this.em = em;
    }
    public void begin() throws NotSupportedException {
        if (em.getTransaction().isActive()) {
            throw new NotSupportedException();
        }
        em.getTransaction().begin();
    }
    public void commit() throws RollbackException {
        try {
            em.getTransaction().commit();
        } catch (javax.persistence.RollbackException e) {
            throw new RollbackException(e.getMessage());
        }
    }
    public void rollback() throws SystemException {
        try {
            em.getTransaction().rollback();
        } catch (PersistenceException e) {
            throw new SystemException(e.getMessage());
        }
    }
    public void setRollbackOnly() {
        em.getTransaction().setRollbackOnly();
    }
    public int getStatus() {
        if (em.getTransaction().isActive()) {
            return Status.STATUS_ACTIVE;
        } else {
            return Status.STATUS_NO_TRANSACTION;
        }
    }
    public void setTransactionTimeout(int timeout) {
        throw new UnsupportedOperationException();
    }
}
Listing 15-13

Emulating UserTransaction Using EntityTransaction

Note that we have implemented setTransactionTimeout() to throw an exception, but this does not necessarily have to be the case. If the transaction timeout is set simply to prevent processes from taking too long to complete, it might be safe to ignore the setting in an integration test.

To demonstrate this wrapper, first consider Listing 15-14, which demonstrates a variation of the example from Listing 15-11 that uses bean-managed transactions instead of container-managed transactions.

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class DepartmentServiceBean implements DepartmentService {
    // ...
    @Resource UserTransaction tx;
    public List assignEmployeeToDepartment(int deptId, int empId) {
        try {
            tx.begin();
            Department dept = em.find(Department.class, deptId);
            Employee emp = em.find(Employee.class, empId);
            dept.getEmployees().add(emp);
            emp.setDepartment(dept);
            tx.commit();
            return em.createQuery(QUERY)
                     .setParameter(1, dept)
                     .getResultList();
        } catch (Exception e) {
            // handle transaction exceptions
            // ...
        }
    }
    // ...
}
Listing 15-14

Using Bean-Managed Transactions

Using the UserTransaction wrapper is simply a matter of injecting it into a session bean that has declared a dependency on UserTransaction. Because the wrapper holds onto an entity manager instance, it can begin and end EntityTransaction transactions as required from within the application code being tested. Listing 15-15 shows the revised test case from Listing 15-12 using this wrapper to emulate bean-managed transactions.

public class DepartmentServiceBeanTest {
    // ...
    @Test
    public void testAssignEmployeeToDepartment() throws Exception {
        DepartmentServiceBean bean = new DepartmentServiceBean();
        bean.em = em;
        bean.tx = new EntityUserTransaction(em);
        List result = bean.assignEmployeeToDepartment(700, 500);
        Assert.assertEquals(2, result.size());
        Assert.assertEquals("John", ((Employee)result.get(0)).getName());
        Assert.assertEquals("Scott", ((Employee)result.get(1)).getName());
    }
    // ...
}
Listing 15-15

Executing a Test with Emulated Bean-Managed Transactions

Note that although the UserTransaction interface is used, that doesn’t mean it’s actually necessary for any particular test. If the transaction state doesn’t affect the outcome of the test, consider using an implementation of the UserTransaction interface that doesn’t do anything. For example, the implementation of UserTransaction shown in Listing 15-16 is fine for any case where transaction demarcation is declared but unnecessary.

public class NullUserTransaction implements UserTransaction {
    public void begin() {}
    public void commit() {}
    public void rollback() {}
    public void setRollbackOnly() {}
    public int getStatus() {
        return Status.STATUS_NO_TRANSACTION;
    }
    public void setTransactionTimeout(int timeout) {}
}
Listing 15-16

A Stubbed UserTransaction

The test case shown in Listing 15-12 could also have tested the bean from Listing 15-14 if the empty UserTransaction wrapper from Listing 15-16 was also injected into the bean instance. This would disable the bean-managed transactions of the actual business method, allowing the transactions of the test case to be used instead.

Container-Managed Entity Managers

The default entity manager type for most beans is container-managed and transaction-scoped, but in the case of stateful session beans they can also be extended. In either case, the goal of testing outside the container is to map the application-managed entity manager used by the test to one of these container-managed entity manager types.

The good news for testing code that uses the extended entity manager is that the application-managed entity manager offers almost exactly the same feature set. It can usually be injected into a stateful session bean instance in place of an extended entity manager, and the business logic should function without change in most cases.

Likewise, most of the time the transaction-scoped entity manager works just fine when an application-managed entity manager is used in its place. The main issue we need to deal with in the case of transaction-scoped entity managers is detachment. When a transaction ends, any managed entities become detached. In terms of a test, that just means that we need to ensure that clear() is invoked on the transaction boundary for our test entity manager.

We may also need to deal with the issue of propagation. In some respects, propagation is easy in a test environment. If you inject the same application-managed entity manager instance into two bean instances, the beans share the same persistence context as if the entity manager were propagated with the transaction. In fact, it is far more likely that you will need to inject multiple entity managers to simulate the intentional lack of propagation (such as a bean that invokes a REQUIRES_NEW method on another bean) than that you will have to do anything special for propagation.

Let’s look at a concrete example of transaction propagation using the examples we introduced in Chapter 6. Listing 15-17 shows the implementation of the AuditService bean that performs audit logging. We used setter injection in this example to contrast it against the version from Chapter 6.

@Stateless
public class AuditService {
    private EntityManager em;
    @PersistenceContext(unitName="hr")
    public void setEntityManager(EntityManager em) {
        this.em = em;
    }
    public void logTransaction(int empNo, String action) {
        // verify employee number is valid
        if (em.find(Employee.class, empNo) == null) {
            throw new IllegalArgumentException("Unknown employee id");
        }
        LogRecord lr = new LogRecord(empNo, action);
        em.persist(lr);
    }
}
Listing 15-17

AuditService Session Bean with Setter Injection

Likewise, Listing 15-18 shows a fragment from the EmployeeService session bean that uses the AuditService session bean to record when a new Employee instance has been persisted. Because both the createEmployee() and logTransaction() methods are invoked in the same transaction without a commit in between, the persistence context must be propagated from one to the other. Again we have used setter injection instead of field injection to make the bean easier to test.

@Stateless
public class EmployeeService {
    EntityManager em;
    AuditService audit;
    @PersistenceContext
    public void setEntityManager(EntityManager em) {
        this.em = em;
    }
    @EJB
    public void setAuditService(AuditService audit) {
        this.audit = audit;
    }
    public void createEmployee(Employee emp) {
        em.persist(emp);
        audit.logTransaction(emp.getId(), "created employee");
    }
    // ...
}
Listing 15-18

EmployeeService Session Bean with Setter Injection

Using the previous two session beans as an example, Listing 15-19 demonstrates how to emulate propagation between two transaction-scoped, container-managed entity managers. The first step to make this testable is to instantiate each session bean. The AuditService bean is then injected into the EmployeeService bean, and the test entity manager instance is injected into both session beans. The injection of the same EntityManager instance effectively propagates any changes from the EmployeeService bean to the AuditService bean. Note that we have also used the entity manager in the test to locate and verify the results of the business method.

public class TestEmployeeService {
    // ...
    @Test
    public void testCreateEmployee() throws Exception {
        EmployeeService empService = new EmployeeService();
        AuditService auditService = new AuditService();
        empService.setEntityManager(em);
        empService.setAuditService(auditService);
        auditService.setEntityManager(em);
        Employee emp = new Employee();
        emp.setId(99);
        emp.setName("Wayne");
        empService.createEmployee(emp);
        emp = em.find(Employee.class, 99);
        Assert.assertNotNull(emp);
        Assert.assertEquals(99, emp.getId());
        Assert.assertEquals("Wayne", emp.getName());
    }
    // ...
}
Listing 15-19

Simulating Container-Managed Transaction Propagation

Other Services

There is more to most beans than just dependency injection and transaction management. For example, as we saw in Chapter 3, beans can also take advantage of lifecycle methods. Other services that are beyond the scope of this book include security management and interceptors.

The general rule is that in a test environment, you need to manually perform the work that would have otherwise been done automatically by the container. In the case of lifecycle methods , for example, you have to explicitly invoke these methods if they are required for a particular test. Given this requirement, it is a good idea to use package or protected scope methods so that they can be manually invoked by test cases.

That being said, be aggressive in determining the true number of things that have to occur in order for a test to succeed. Just because security roles have been declared for a session bean method doesn’t mean that it actually has any effect on the test outcome. If it doesn’t have to be invoked prior to the test, don’t waste time setting up the test environment to make it happen.

Using an Embedded EJB Container for Integration Testing

When multiple session beans collaborate to implement a particular application use case, a lot of scaffolding code may be required to get things up and running. If multiple test cases share similar graphs of session beans, some or all of this code may have to be duplicated across multiple test cases. Ideally, we want a framework to assist with issues such as dependency injection in our test environment.

Fortunately, EJB supports just such a container. An embedded EJB container supports EJB Lite, a subset of the overall EJB feature set. EJB Lite includes support for local session beans, interceptors, container-managed transactions (assuming the availability of a stand-alone JTA transaction manager implementation) and security, and JPA, but does not include support for remote session beans, message-driven beans, web service endpoints, timers, or asynchronous session beans. It offers more than enough to handle the different dependency scenarios we have described so far.

To demonstrate how to use an embedded EJB container for integration testing with session beans and the entity manager, we revisit the propagation test case from the preceding “Container-Managed Entity Managers” section and convert it to use an embedded container.

Tip

Embedded EJB containers were introduced in EJB 3.1.

Unlike the other forms of integration techniques we have looked at so far, an embedded EJB container requires no mocking of standard interfaces or subclassing of beans to override behavior specific to the server. As long as an application fits within the subset of the EJB specification supported by embedded containers, it can be used as-is.

Bootstrapping the embedded container is straightforward. You compile and package the classes as normal into an EJB JAR file and add that JAR file to the test classpath in order for the embedded container bootstrap mechanism to locate it. The static createEJBContainer() method of the javax.ejb.embeddable.EJBContainer class can then be used to create an EJB container and load the module from the classpath. Listing 15-20 demonstrates the bootstrapping process. Additional options for the container may be specified by passing in a Map of properties, but for basic tests with a single module, no special configuration is required.

public class TestEmployeeService {
    private EJBContainer container;
    @Before
    public void setUp() {
        container = EJBContainer.createEJBContainer();
    }
    @After
    public void tearDown() {
        container.close();
    }
    // ...
}
Listing 15-20

Bootstrapping an Embedded EJB Container Within a Test Case

Once the container has initialized, we need to get access to session beans in order to test them. The embedded container exposes its internal JNDI directory of session beans via the getContext() method of the EJBContainer class. The test code must then make a global JNDI lookup in order to access a particular session bean. A global lookup does not require references or an environment naming context. Instead, the module name (derived from the JAR name) and session bean names are composed to form a unique name under the JNDI root “global”. Listing 15-21 demonstrates this technique assuming the beans have been packaged in an EJB jar file called hr.jar.

public class TestEmployeeService extends TestCase {
    private EJBContainer container;
    // ...
    private EmployeeService getServiceBean() throws Exception {
        return (EmployeeService) container.getContext().lookup("java:global/hr/EmployeeService");
    }
    private EntityManager getEntityManager() throws Exception {
        return (EntityManager) container.getContext().lookup("java:global/hr/HRService");
    }
    // ...
}
Listing 15-21

Acquiring a Session Bean Reference from an Embedded EJB Container

With access to a live session bean, we can now write a test methods as if we were running code directly within the application server. Listing 15-22 completes this example with a new version of testCreateEmployee() that uses the bean reference from the embedded container.

public class TestEmployeeService {
    // ...
    @Test
    public void testCreateEmployee() throws Exception {
        EmployeeService bean = getServiceBean();
        Employee emp = new Employee();
        emp.setId(99);
        emp.setName("Wayne");
        bean.createEmployee(emp);
        EntityManager em = getEntityManager();
        emp = em.find(Employee.class, 99);
        Assert.assertNotNull(emp);
        Assert.assertEquals(99, emp.getId());
        Assert.assertEquals("Wayne", emp.getName());
    }
    // ...
}
Listing 15-22

Testing a Session Bean Acquired from an Embedded EJB Container

As discussed earlier in the “Switching Configurations for Testing” section, a custom persistence.xml file appropriate to the test environment may be required and should be packaged in the EJB jar file used on the system classpath. Likewise, sharing the EJB container across test executions will likely improve overall test suite performance, but again care must be taken not to accidentally influence the outcome of other tests with state maintained by the EJB container.

For two session beans, this approach is arguably overkill compared with the same test case shown in Listing 15-19. But it should be easy to see even from this small example how complex bean relationships can be realized using an embedded EJB container.

Test Frameworks

As long as technologies continue to get created and evolve, test frameworks will be right behind, trying to make the testing of code that uses those technologies more manageable. Since the release of JPA a few frameworks have surfaced with varying levels of JPA support to enable integration testing. While the details of using these frameworks is beyond the scope of this book, we thought it might be beneficial to at least mention and provide a pointer to them. You can then go off and do some exploring on your own to see if any of them will meet your needs. If you hear of others that have specific support for JPA but that are not included in this section, please let us know, and we can include them in subsequent editions of the book.

JPA-Unit

The JPA-Unit framework ( https://code.google.com/p/jpa-unit ) is a simple little test framework that primarily supports testing non-container JPA applications. It doesn't support JTA transactions so it's somewhat limited. It is integrated with JUnit, and tests are added by subclassing a JPA-Unit framework test case class. It provides injection of an entity manager and support for automatic pre-loading of entity data based on a custom-formatted XML data file. This may be sufficient for an application with very simple requirements, but is not likely to offer enough for the majority of enterprise applications. It also doesn't seem to be very regularly maintained and hasn't had any updates or action on its website for over 1.5 years as of this writing.

Spring TestContext

The TestContext framework in Spring is not a uniquely JPA framework, but it does include the Spring support for injection of JPA objects like the entity manager and entity manager factory (using the standard @PersistenceContext and @PersistenceUnit annotations). It can integrate with JUnit, TestNG, EasyMock, and other unit testing frameworks when it comes time for test definition and execution. Because so much of the framework is based on the testing of Spring components , though, adopting its test model won't make as much sense if your application is not Spring-based. For applications that do use Spring Data JPA, this framework might be worth investigating.

Arquillian

For testing Java EE applications that use CDI beans or EJBs to manipulate JPA entities, the Arquillian framework ( http://arquillian.org ) could be your best bet for an integration testing framework.

In general, Arquillian can test the following:
  • Security

  • CDI/EJB3

  • JPA

  • JAX-RS/JAX-WS/WebSockets

  • JSF/JSPs/Servlets

Arquillian testing framework is composed of three parts:
  • Test runners such as JUnit or TestNG

  • Containers

  • Test enrichers

Here are the most common and most used Arquillian extensions:
  • Persistence

  • Drone

  • Graphene

  • Warp

  • Performance

Arquillian provides support for running tests in a non-server environment (using stand-alone CDI and EJB containers) as well as running tests within the container. It integrates with JUnit and TestNG, and fits in reasonably well with those testing models. Because it packages up the test classes and necessary framework infrastructure classes into an archive and even deploys it into the server, there is an additional method that must be supplied to define what bits and pieces need to be included in that application archive. The rest is fairly easy to do once the environment has been correctly configured.

Arquillian has a couple of dependencies that should be known beforehand, though. The first is on ShrinkWrap, a JBoss tool that packages artifacts up into an archive. The second is on the existence of a container adapter for the container being deployed to. Not all containers are supported, so if you are deploying to an unsupported target server, you could be out of luck. Check the website to see if your container is supported.

Best Practices

A full discussion of developer testing strategies is beyond the scope of this chapter, but to make testing of application code that uses entities easier, consider adopting the following best practices:
  • Avoid using the entity manager from within entity classes. This creates a tight coupling between the domain object and the persistence API, making testing difficult. Queries that are related to an entity, but not part of its object-relational mapping, are better executed within a session façade or data access object.

  • Use dependency injection over JNDI lookups in referencing beans. Dependency injection is a key technology for simplifying tests. Instead of mocking the JNDI interfaces to provide runtime support for unit testing, the required values can be directly assigned to the object using a setter method or field access. Note that accessing private fields from a test case is bad form. Either use package private fields as the target for injected objects or provide a setter method.

  • Isolate persistence operations. Keeping EntityManager and Query operations separate in their own methods makes replacing them easier during unit testing.

  • Refactor when necessary. Don’t be afraid to refactor application code to make it more test-friendly so long as the refactoring benefits the application as a whole. Method extraction, parameter introduction, and other refactoring techniques can help break down complex application logic into testable chunks, improving the overall readability and maintainability of the application in the process.

  • Use an existing framework when possible. If a framework already does most of what you need to do, clearly you can save time and resources by making use of it and possibly just adding the extra bits that you need. It may even be appropriate to propose those changes back to the framework committers for inclusion in a subsequent framework release.

These approaches can help support your testing style, regardless of the approach you choose to use.

Note

There are a lot of nice Java EE 8 examples in GitHub for testing purposes:

https://github.com/javaee-samples/javaee8-samples

Summary

In this chapter, we started with an exploration of testing enterprise applications and the challenges that have traditionally faced developers. We also looked at the different types of testing performed by developers, quality engineers, and customers; and we refined our focus to look specifically at developer tests for JPA applications.

In the section on unit testing, we looked at how to test entity classes and then pulled back to look at how to test beans in combination with entities in a unit test environment. We introduced the concept of mock objects and explored how to test code that depends on the entity manager without actually using a real entity manager.

In our discussion of integration testing, we discussed how to get the entity manager up and running in JUnit tests in the Java SE environment and the situations where it makes sense to use this technique. We covered a number of issues related to the entity manager, including how to safely seed a database for testing, how to use multiple mapping files for different database configurations, and how to minimize the number of database connections required for a test suite. We also looked at how to use an embedded EJB container for integration testing.

We looked at how to use session beans in integration tests and how to deal with dependency-injection and transaction-management issues. For transaction management, we looked at how to emulate container-managed and bean-managed transactions, as well as how to simulate persistence context propagation in a test environment. We concluded with a short survey of JPA test frameworks and a summary of some best practices to consider when building Java EE applications using JPA.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.98.108