EJB 3 In Action - Putting EJB into action - Testing and EJB

463 阅读5分钟

Introducing testing

  • Unit testing
  • Integration testing
  • Functional testing

Unit Testing

  • No connections to outside resources are allowed (databases, web services, and so on).
  • Test only a single class at a time.
  • Test only a class’s public contract (public methods or interface) and vary test input data to cover any private code.
  • Mock dependencies to return the data required to test the business logic of the class being tested.

JUnit is a popular testing framework for Java applications. Mockito is a powerful and easy-to-use framework for mocking a class’s dependencies for unit tests. These two technologies, combined with the convention-over-configuration and POJO models of EJBs, have made unit testing EJBs much easier.

Integration testing

Its primary purpose is to ensure that the interaction of your code with external resources is correct, and that when all of the different technologies used in the application actually come together, they can function as they’re intended to.

In an integration testing strategy, you don’t mock the data as you do in a unit test. Instead, the test will most likely use an in-memory database that can be easily cre- ated and destroyed for the lifecycle of the integration tests.

  • The embedded EJBContainer can also be utilized by integration tests for testing EJBs

  • The integration test can start the embedded EJBContainer in-memory, which will deploy all the EJBs it finds in the integration test’s class path and then proceed with testing the EJBs.

  • Arquillian is a sophisticated integrated testing tool. Think of it as a wrapper around the embedded EJBContainer . It presents a simpler interface for configuring and run- ning the embedded EJBContainer .

Functional testing

At this level, the application is fully deployed into a real environment. The testing team will use a combination of automated and manual testing to check that the application is working properly.

Unit testing EJBs

  • Example

image.png

  • Unit Test

image.png

Integration testing using embedded EJBContainer

You’re going to start the embedded container at the beginning of the test with the help of the builder method on the EJBContainer object, as follows:

EJBContainer ejbContainer = EJBContainer.createEJBContainer();

Once you have an instance of EJBContainer , you can use it to get a Context:

Context ctx = ejbContainer.getContext();

After you have a Context, you’re free to look up any bean bound in JNDI.

DiscountManager manager = (DiscountManager) ctx.lookup(
  "java:global/chapter15-ejb-1.0/DiscountManagerBean");

Project configuration

Before getting into the code for the integration test, you first need to set up and configure the project to run it.

Integration tests will usually require a lot more complex configuration, and because the tests will be running in an embedded container, they usually will require a lot longer to run. Because of their complexity and time needed to run, integration tests are typically put into their own projects. This is what you’re going to do for this integration test example. The code for this chapter contains a Maven submodule named chapter15-ejb-embedded-test, which is where you’ll put the integration tests that use the embedded EJBContainer.

Will my integration tests ever run?

Unit tests inside a project are executed by Maven by default.

But if the integration tests are in a separate project, a developer will need to take an extra manual step to run the tests, and most of the time this step will be skipped.

This is where tools like Bamboo, Jenkins, and other automatic build platforms come in. The best practice is to configure these tools to run the integration tests automat- ically once changes to the project have been detected. Alternatively, instead of doing them on demand, they can be scheduled to run off hours, typically in the evening.

By using tools like this, you remove the responsibility of running the integration tests from the development team and put it onto the tool. But if integration tests fail, then it becomes the developer’s responsibility again.

How to configure the test project

  • Configure maven-surefire-plugin and pass a module name to the integration test.
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.10</version>
      <configuration>
        <systemProperties>
          <property>
            <name>moduleName</name>
            <value>chapter15-ejb-${chapter15-ejb.version}</value>
          </property>
        </systemProperties>
      </configuration>
    </plugin>
  </plugins>
</build>
  • The GlassFish implementation of the embedded EJBContainer
<dependency>
    <groupId>org.glassfish.extras</groupId>
    <artifactId>glassfish-embedded-all</artifactId>
    <version>3.1</version>
    <scope>test</scope>
</dependency>
  • Glassfish config

  • JPA config

Integration test

image.png

image.png

Integration testing using Arquillian

  • Arquillian makes integration testing with embedded containers a little easier so you’re able to concentrate more on the integration tests itself and not on all the plumbing needed to get it working.
  • Arquillian also makes more complicated integration testing scenarios easier.
    • Arquillian has shrink-wraps for multiple Enterprise application servers
    • Arquillian also supports remote integration testing.

Project configuration

  • Maven
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.jboss.arquillian</groupId>
      <artifactId>arquillian-bom</artifactId>
      <version>1.1.0.Final</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

JUnit will simply be a technology to basically bootstrap Arquillian and run the integration tests:

<dependency>
  <groupId>org.jboss.arquillian.junit</groupId>
  <artifactId>arquillian-junit-container</artifactId>
  <scope>test</scope>
</dependency>

Finally, you want to get the Arquillian wrappers over GlassFish because you’ll be using the GlassFish implementation of the embedded EJBContainer for the integration tests:

<dependency>
  <groupId>org.jboss.arquillian.container</groupId>
  <artifactId>arquillian-glassfish-embedded-3.1</artifactId>
  <version>1.0.0.CR3</version>
</dependency>

Two additional dependen- cies we need to look at are the dependencies to get the EJBs from the chapter15-ejb project (which you need to test) and glassfish-embedded-all for GlassFish.

<dependency>
  <groupId>com.actionbazaar</groupId>
  <artifactId>chapter15-ejb</artifactId>
  <version>1.0</version>
  <scope>test</scope>
</dependency>

<dependency>
  <groupId>org.glassfish.extras</groupId>
  <artifactId>glassfish-embedded-all</artifactId>
  <version>3.1</version>
  <scope>test</scope>
</dependency>

Arquillian

Arquillian’s configuration file is src/test/resources/ arquillian.xml. Because you want to run the integration tests with GlassFish, you need to tell Arquillian where to find the GlassFish resource configuration for the integration test. This is a simple property in arquillian.xml pointing to the glassfish-resources.xml file:

<arquillian xmlns="http://jboss.org/schema/arquillian"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="
        http://jboss.org/schema/arquillian
        http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
  <container qualifier="glassfish-embedded" default="true">
    <configuration>
      <property name="resourcesXml">
        src/test/glassfish/glassfish-resources.xml
      </property>
    </configuration>
  </container>
</arquillian>

Glashfish and derby

For the Arquillian integration test, you need to provide the src/test/glassfish/glassfish-resources.xml file. This file defines a jdbc-connection- pool for GlassFish and binds it in JNDI to jdbc/chapter15-ejb-arquillian:

<!DOCTYPE resources PUBLIC
   "-//GlassFish.org//DTD GlassFish Application Server 3.1
    Resource Definitions//EN"
   "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
  <jdbc-resource pool-name="ArquillianEmbeddedDerbyPool"
    jndi-name="jdbc/chapter15-ejb-arquillian"/>
  <jdbc-connection-pool name="ArquillianEmbeddedDerbyPool"
    res-type="javax.sql.DataSource"
    datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource"
    is-isolation-level-guaranteed="false">
    <property name="databaseName"
      value="target/derby/arquillian-integration-test"/>
    <property name="createDatabase" value="create"/>
  </jdbc-connection-pool>
</resources>

JPA

JPA is configured with src/test/resources/META-INF/persistence.xml.

The only difference is the JNDI lookup value for the data source. The Arquillian integration test binds it in JNDI to jdbc/chapter15-ejb- arquillian, so persistence.xml reflects this:

<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
  http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="ActionBazaar" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/chapter15-ejb-arquillian</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property
        name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

Intergration test

image.png

Testing effectively

To test your code effectively, you’ll need multiple levels of testing. Each level is focused on testing a specific aspect of your code.

  • Unit test

    • The focus of unit testing is to verify that all of the business logic of all of your classes is working as expected.
    • The strategy for doing this is to write tests to focus only on one class, mock whatever dependencies the class has, and vary both the data input to the class and the data returned by the mock to cover all possible business rules.
    • Assert results returned from the class being tested against expected results.
    • Whenever you mock a class that’s also part of your application, you need to make sure you provide unit tests for that class as well so all of the classes in your application are covered.
    • Unit tests shouldn’t require a lot of setup and should be run quickly so broken business rules will be discovered immediately.
    • Unit tests shouldn’t rely on any external resources (database, web service, naming directory, and so on) to execute; all interaction with external services like this should be mocked returning appropriate data for the unit test.
    • Keeping unit testing focused like this can result in a 100% covered green bar suite of tests on code that’s completely undeployable to any Java EE server.
  • Integration test

    • Verify that all of the different technologies used in the application can actually come together and function properly as a whole.
    • Does the data persist to the database?
    • Will the EJB be deployed?
    • Will the interceptors fire in the order I expect them?
    • Verify that the individual classes can come together (inside a container) as a whole and function as you expect them to
    • Unlike unit tests that should remain in the projects containing the code the unit tests cover, integration tests are best put into their own project.
    • Maven dependency injection can get all the code that needs to be integration tested, and continuous build tools like Bamboo or Jenkins ensure the integration tests get run.
  • Functional test

    • Falls on a team of individuals whose job is to use the application and verify that it’s working properly
    • Application is fully deployed to some testing environment that hopefully closely matches a production environment.
    • The last line of defense for logical problems with your application.