EJB Java still has an application area

Table of Contents

introduction

This document shows how to test session beans using the procedures described in the document "Testing Enterprise Java Beans 3.1". In addition to explanations of the various options for testing the session beans as plain old Java tests (POJT) within an embedded container and on the actual application server, the restrictions and the problems that arise should also be discussed in particular. Different examples of the individual procedures are used for demonstration purposes; the source files can be downloaded as RAR archives.

The following tools were used for the implementation:

  • JBoss AS 7.1
  • JUnit 4.10
  • Mockito 1.9.5
  • OpenEJB 4.5.1

Testing using simple plain old Java tests

The testing of the individual beans as POJT is the easiest and fastest possibility to examine the developed business logic for correctness. However, the scope of the POJT is very limited. As already described in the article on testing EJBs, such tests only work as long as no application server services are used.

Session beans often use additional session beans or one or more different entities for the complete implementation of the client request or the actual task. If, for example, another required session bean accesses the services of the application server within such a construct, while the session bean to be tested does not require additional environmental services, then, for example, the creation of mock objects for a test of the business logic is possible, but this option also brings further difficulties as will be shown below.

Calculator as Stateless SessionBean

The first example shows the test for a simple stateless session bean, which implements the functions of a calculator for adding, subtracting, multiplying and dividing two numbers.



Since EJB version 3.0, EJBs have again been plain old Java objects (POJOs) and associated remote and local interfaces again represent plain old Java interfaces (POJIs), which means that beans can be generated using the default constructor. For this reason, the JUnit tests do not differ much from the known test cases outside of the enterprise area. The following excerpt from the source code shows how JUnit test cases can be designed for the stateless session bean presented.

Calculator as a stateful session bean

In a further example, stateful session beans are to be considered. For this purpose, the calculator bean from the previous example has been expanded and the methods have been adapted accordingly. The following figures show the source code of the Stateful Calculator session bean and the remote interface used.



From the point of view of the test, the stateful session beans do not differ significantly from the stateless session beans. As with the stateless session bean, the business logic is encapsulated in the methods and can be accessed directly from the JUnit tests. The biggest difference is the client-specific status that the Stateful SessionBean has. However, since it can always be assumed that the application server is working and the session bean is able to maintain the state across multiple calls, the logic of the stateful session bean can be tested directly by the JUnit tests without using the server. The JUnit test checks whether calling the business methods has the desired effects. However, this test procedure cannot determine whether the stateful session bean also exhibits the desired behavior in an EJB container. In order to ensure the desired behavior, integration tests are required in addition to tests in an embedded container or on the application server. The following excerpt from the source code shows a possible JUnit test for the stateful session bean.



Access to further session beans and use of container services

The third example for testing using Plain Old Java Tests shows both the difficulties and the limits of the test procedure. For this example, a stateless session bean was developed for calculating interest, which determines the corresponding interest rate from an amount and the term and returns the future total amount to the client. The following source code shows the implementation.



To determine the interest rate, the session bean "ZinsrechnerImpl" uses another session bean, "Zinssatz", which uses the services of the application server to generate various runtime interest rate combinations and temporarily stores them in the cache. The following source code shows the implementation of the interest rate session bean.



From the developer's point of view, critical sections arise at several points that make it difficult to implement a POJT:

1. Access to the services of the application server
The first problem arises from the interest rate bean used by the interest rate calculator bean. The interest rate bean accesses the services of the application server in several places to calculate the interest rate. In this case, the SessionContext is used to create a timer that defines the validity for the interest rate cache map, which contains the calculated interest rates with the corresponding terms. After the timer expires, the map becomes invalid and the list is emptied by the method. The annotation ensures that this method is called automatically when the event occurs. This and the functionality of the SessionContext are not available in a JUnit test using a simple POJT and would have to be implemented by developers if required. However, since the developer quickly redeveloped an application server, this class is not suitable for testing using POJT. However, in order to check the construction of the session beans and in particular the business logic of the interest rate calculator bean, a mock object of the interest rate bean can be created. In such a case, for example, a mock object of the interest rate bean can be generated and the specified return values ​​of the function specified. If the mock object is given an amount of 10,000 € with a term of three years when the function is called, the mock object can return the value 0.35 previously set by the developer. The example associated with this chapter shows a complete implementation using mock objects.

2. Life cycle of a session bean
Another challenge for the developer arises from the fact that a session bean is not simply instantiated via a constructor, but runs through a life cycle. Therefore, all methods that are otherwise called by the application server when running through the life cycle must be explicitly called manually after construction. A further complication is that these callback methods can not only be defined within the session bean itself, but can also be implemented within an interceptor. After instantiating the bean and resolving the dependencies using dependency injection, the lifecycle callback method is called after the lifecycle has been defined. In this example, the method annotated with the annotation is called, which determines the interest rate bean using JNDI and sets the instance variable for the interest rate bean accordingly. From the point of view of a JUnit test, this lifecycle callback method cannot be called because the name and directory services of the application server are not available. The method annotated with can be ignored by the developer, since it only executes one output command. In the event that important steps for the business logic also take place here, the developer must also call this method explicitly. The developer must also take care of the instantiation of the instance variable of the interest calculator bean during a JUnit test. However, the dependency injection also represents a stumbling block for the developer, as the next point shows.

3. Dependency injection
The dependency injection performed by the developer can lead to further problems when creating tests. In this example, the variable interest rate is declared as, which is why it can easily be set from the outside. However, if the attribute has been declared as, then the inventiveness of the developer is required. For example, in order to set the attributes accordingly, a setter injection can be implemented that can be used by both the container and developers.

To test the business logic of the interest calculator bean, more effort is required than was the case with the previous examples. For example, a JUnit test could look like this.

Since the interest rate bean cannot be part of the JUnit test due to the close integration with the services of the application server, a mock object is first created from this session bean using Mockito, which is set via dependency injection. Various combinations of the transfer parameters and the corresponding return values ​​of the method are defined for the mock object, which can then be used by the interest calculator bean for further processing. At this point, it must be assumed that the logic of the interest rate bean works correctly and that it is ensured that no errors are made when defining the mock object, as these can lead to incorrect results when processing within the business logic of the interest rate calculator bean .

Testing within an embedded container

If EJBs use the services of the container, testing in an environment that is as real as possible is essential. Instead of re-implementing these services and thus losing valuable time that should otherwise be used for extensive testing, so-called embedded containers can be used. The advantages of such solutions are obvious. On the one hand, services of the environment can be used and thus the logic implemented by the developer for interaction with the server can be tested in a real environment; on the other hand, such an embedded container works much faster than the application server such as JBoss, so that a test run is much faster and can be carried out without explicit deployment and undeployment. Candidates for such tests are, for example, CUBA or OpenEJB. OpenEJB version 4.5.1 is used for this chapter. In the previous chapter, the limits of testing using POJT were shown, which were particularly important in the third example, "Interest calculator". This chapter shows one way how this example can be fully tested. Conventional JUnit tests can also be used when using the OpenEJB embedded container, as the following source code excerpt shows.

The properties for the must be created and set within the method. Calling the first method creates a, which ensures that the OpenEJB container is started when the test is run. After starting the container, it searches all class path entries for EJBs that either contain a deployment descriptor or that have been transferred using the parameter and makes the EJBs available accordingly. The parameter can be used to explicitly exclude class paths.

The first test case shows an example of how the EJBs can be determined with OpenEJB. The JNDI entry is made up of and, accordingly the name of the bean and either Local for the local or Remote for the remote interface. This is the default variant of OpenEJB, but it can be modified as required, as the website shows.

Difficulties can arise especially when session beans perform lookups from other beans and the name or path does not match the OpenEJB schema. The following figure shows such a call in the method of the interest calculator bean.

The path "" represents the path for finding EJBs in JBoss. However, this cannot be used in this form by OpenEJB and in JUnit test runs inevitably leads to errors in all tests that use the EJB to be determined. Changing all paths for a - possibly even automated - test is not a satisfactory solution. Rather, it is advisable to use a format that can be interpreted by both JBoss and OpenEJB. Two different approaches are presented below.

Dependency lookup

To use the dependency lookup, the annotation must be set before the class declaration. As the following figure shows, both the name, the bean name and the bean interface can be set accordingly.

The optional name specifies the path under which the referenced bean can be found in the local namespace () of the component. The bean name represents the logical name of the referenced bean, which is defined either by the class name or the name in the annotations, and. The interface type of the referenced bean is specified by the bean interface. This can be a local or a remote interface.

The disadvantage of the dependency lookup is that the technical source code is soiled with technical details. The developer has to take care of the reference and catch exceptions from the name service and handle them accordingly.

Dependency injection

When using dependency injection, the responsibility for finding the references is passed on to the application server, which is also known as inversion-of-control, since the class that needs the reference only uses it and it is used by the caller of the class leaves to obtain and set the reference. This also results in the first big difference in implementation in contrast to the dependency lookup. An explicit lookup is no longer required within the method. The annotation is also used, but is set in the case of dependency injection within the class definition and above the instance or class variable. The following figure shows this as an example.

The meaning of the parameters is identical to that when using the dependency lookup, but the "name" parameter is omitted in this case. Corresponding entries within the deployment descriptor can be used instead of annotations for both dependency injection and dependency lookup.

Testing the installed application in the application server

For testing on the application server, only the client calls or the JUnit test cases need to be adapted. In the following a test case is exemplified for the interest calculator, which is on the JBoss AS 7.1. Application server is provided, presented and explained accordingly. The disadvantages of this procedure are not fully apparent from the source code, which is why they are briefly summarized again before the actual example is dealt with. The application server is operated at a central point and is not started by means of or by a JUnit test case. The deployment also does not take place as part of the JUnit test case, which means that deployment must take place in advance. The application server must also be started up and ready for operation before the test, which makes "fast" testing impossible. If an error has been discovered by a test case, the source files must be edited and the updated version deployed on the server before a new test run can be started. Before testing on the application server, it is therefore advisable to first carry out simple POJT and then tests within an embedded container in order to be able to rule out as many errors as possible in advance, which can result in enormous savings potential in the otherwise very limited resource time. The following source code excerpts show the test case for the interest calculator bean on a JBoss AS 7.1 application server.



The JUnit test case is executed within the class. To do this, a lookup of the bean is first carried out within the method. In the middle of the method, the. For this purpose, the class is used that is shown in the second source code excerpt and whose methods are already known from the previous sections. The method is called to get the correct string to determine the bean searched for on the JBoss AS 7.1 server. The general path for calling a bean on a JBoss AS 7.1 server is defined by "" for a Stateless SessionBean and "" for a Stateful SeassionBeam, which is composed accordingly in the method. The test case is then executed in the method testeSparsumum annotated with.

credentials

[01] W. Everling, J. Leßner: Enterprise JavaBeans 3.1: Das EjB3-Praxisbuch for beginners and migrants, 2nd edition, Carl Hanser Verlag, Munich, 2011
O. Ihns, S. Heldt, et. al: EJB 3.1 professional - basics and expert knowledge of Enterprise JavaBeans 3.1 - including JPA 2.0, 2nd, updated and expanded edition, dpunkt.verlag, Heidelberg, 2011

Back to the tools for JEE applications
Back to the tools