Categories
Atbash Testing

Support for Database and Jakarta EE 10 within Jakarta EE Integration Testing Framework

In this release of the Atbash Integration testing framework, we continued improving the support for creating customised Docker images that run your application under test but also added 2 major features, support for databases and Jakarta EE 10.

Depending on the target version of your application, you can use version 1.2.0 if you are building a Jakarta EE 8 application, or 2.2.0 if your application depends on Jakarta EE 10. More on that later in this blog.

In previous blogs, we covered the basics of using the Integration testing framework. You can have a look at this blog for the introduction and the functionality that was provided in version 1.0.

With the second release, we added the option to use a custom Docker build file that will be used for the container with the application. And we added support for using Wiremock to mock the response of remote services. More details can be found in this blog.

Update : Presentation at Jakarta Tech Talks which can be viewed on YouTube channel.

Customised Build file

In this release, we continued adding support for customising the container that runs your application and that you want to test. You had already the option to supply a custom Docker Build file where the framework only adds the appropriate Docker ADD command to add the WAR file. Now you also can customise the build file just before it will be sent to the Docker engine for processing.

There is a DockerImageAdapter interface that can be implemented by the developer to adjust a few lines of the build file, including the default build file provided by the framework.

This allows for simple additions that are future-compatible with possible changes that will be made to the default build file in future versions.

Implementations of the interface are picked up by the ServiceLoader mechanism within Java. So don’t forget to define your class name in the appropriate service file.

Since there might be more than one implementation within the classpath, you can order them by adding a @Priority annotation. The implementations with a low value are called first. If no priority value is specified, a default value of 100 is taken. And of course, when multiple implementations have the same value, the exact order is undefined for the matching priority values.

When you only need to add some environment variables to the container, there is no need anymore to define a custom build file. You can make use of the AdditionalEnvParameters class. Within an implementation of the DockerImageAdapter you can access this class as follows

   public String adapt(String dockerFileContent, TestContext testContext) {
      AdditionalEnvParameters envParameters = testContext.getInstance(AdditionalEnvParameters.class);
        if (envParameters == null) {
            AdditionalEnvParameters envParameters = new AdditionalEnvParameters();
            testContext.addInstance(envParameters);
        }
        envParameters.add(key, value);

   }

The TestContext is an object that is added in this release and can be consulted for various information about the test that is running.
It holds for example the ContainerAdapterMetaData instance that holds all data about the test that is currently running.

And with the AdditionalEnvParameters.add(), you can add key-value pairs to the environment variable for the container.

Support for a database

In this version, it is now possible to define a database container and provide the configuration as a data source for the JPA functionality of your application.

To have this functionality, make sure you perform the following steps.

  • Add the Atbash Integration testing Database artefact to your project.
  • Add the @DatabaseContainerIntegrationTest annotation to the test class. The class must also extend from AbstractDatabaseContainerIntegrationTest.
  • Add the Database test container of your choice as a project dependency. This determines the database that will be started alongside the container with your application.
  • Add the JDBC Driver as the test scope. This must be a driver that works with the selected database. The driver is used for 2 purposes, it is added to the container with the application to provide access to the database and it allows during the setup of the test to prepare the database in a known state for the test. You can also access the database tables during the test through this Driver and DBUnit which is proved automatically.

Once these things are in place, a test execution performs the following tasks.

  • Determine the Database container to start by looking at which JDBC database container is on the classpath.
  • Determinate JDBC driver
  • Add the JDBC Driver to the application container in a runtime-specific way.
  • Start the database container
  • Provide the connection settings as environment variables to the application container
  • Create the tables required for the test by executing a SQL script that the developer provided
  • Add the data to the tables by reading the Excel file.

A variable is prepared that allows you to access the database tables during the execution of your test. For this, the functionality of the DBUnit library is used. This allows you to check that a call to an application endpoint resulted in the expected database changes.

Have a look at the examples https://github.com/atbashEE/jakarta-integration-testing/tree/main/example/database to get started with this new exciting feature.

Jakarta EE 10

Until now, The Jakarta EE testing framework supported only Jakarta EE 8. There is now also an artifact available specifically for Jakarta EE 10. version 2.x is now based on Jakarta EE 10 dependencies and thus can be added as a test dependency to your project.

All functionality that is available for Jakarta EE 8, is also available for this EE 10 version.

Conclusion

With this new release of the testing framework, more options are available to customise the container image that runs your application during the test. For example, you have now the possibility to specify those environment variables and values that need to the added without using a custom docker build script.

The two major features that are added to this version are of course the integration with a database container that makes it much easier when your application uses a database to test its functionality and the support for Jakarta EE 10.

Enjoy

Training and Support

Do you need a specific training session on Jakarta or MicroProfile? Or do you need some help in getting up and running with your next project? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Categories
Jakarta EE release Testing

Atbash Jakarta EE Integration Testing version 1.1 Released.

The first version of the integration testing framework was released in July of this year. It allowed you to deploy your application using the Jakarta Runtime of your choice to a Docker environment using the TestContainer framework.

Selecting the runtime is as easy as setting a member value of the test annotation, the runtime version can be specified, endpoints can be easily called based on a MicroProfile Rest Client interface definition and it allows to have remote debugging by just setting a flag.

In this version1.1, you have the option to define the Docker build file that will be used for the test, and features for testing microservices are added.

Custom builds

In the initial version, the official docker images of the Jakarta runtimes are used to run your application within the docker container. But in many cases, your application needs a specific image as it requires some additional content.

This can now be achieved by specifying a custom build file for Docker.

@ContainerIntegrationTest(runtime = SupportedRuntime.PAYARA_MICRO)
@CustomBuildFile(location = "custom/payara")
public class CustomPayaraIT extends AbstractContainerIntegrationTest {
}

The annotation defines that the content of the directory src/docker/custom/payara will be used to generate the Docker image that will be used to test your application. The build file is expected as a file called Dockerfile (if this is not present, the runtime default will be used) and all other files in that directory and subdirectories will be part of the build step. This allows you to have a fully customized Docker image for testing.

Of course, by defining the runtime like Payara Micro in the above example, the port assignment checks to see if the application is ready and debug setup is still taken from the indicated runtime. So make sure you specify the correct runtime in the @ContainerIntegrationTest annotation which corresponds to the system used within the custom Docker build file.

This functionality is especially useful when you are working with Glassfish since they don’t have any official Docker image and support was limited in the first release for this runtime.

Microservices Testing

In most cases, your application depends on other services. They are called to retrieve data for or process parts of the functionality required for the user request. There are 2 additions available in this version that makes it easier to test your application in this situation.

Each public static variable that is a startle test container and added with @Container is also started just before the execution of your test code. It is now easier in this version 1.1 when this additional container is running an application using one of the supported Jakarta runtimes. You only need to specify the docker image name and the setup like health checks and port mappings is taken care of for you automatically.

  @Container
  public static GenericContainer<?> remote = new PayaraMicroContainer(DockerImageName.parse("test-remote:1.0"));

Also, networking configuration is taken care of, as it will share the same network as your application and this container will also have the name of the variable ‘remote’ in this case, as a hostname alias.

This makes it easy to add an external service needed for your application.

But instead of having an entire application providing data for your application, you can also configure the responses for URLs. In other words, you can mock the responses for certain requests for external services. For this purpose, Wiremock functionality is integrated into the framework.

First of all, you define a container that will be running the WireMock application where you also define the hostname which will be used.

  @Container
  public static final WireMockContainer wireMockContainer = WireMockContainer.forHost("wire");

And within each test method, you can define the responses using a builder pattern

  MappingBuilder mappingBuilder = new MappingBuilder()
   .forURL("/path")
   .withBody(foo);

  String mappingId = wireMockContainer.configureResponse(mappingBuilder);

The variable foo can be a String and in that case, the response is a plain text response with the content of the variable as the body. Otherwise, the variable is serialised to JSON and a JSON-type response is prepared.

The builder also has some other methods to control the response like the HTTP status, media type, etc …

Integration testing

With the initial release, you had all the features required to test your application with the Jakarta runtime of your choice using Testcontainers and a docker environment.

In this update, the container running your application under test can now be fully customised using a build file and resources that you specify by indicating the directory where they can be found.

Also, testing applications that are part of a microservices environment got easier. When the eternal service is also a Jakarta application, you only need to indicate its docker image name, and all other configuration is taken care of for you by default.

You can also use Wiremock to define predefined responses for certain requests. Again, the network setup is greatly simplified, and defining responses is easy. JSON responses can be specified by providing the data as a Java Object that will be serialised so that you don’t need to write complex JSON strings.

Atbash Training and Support

Do you need a specific training session on Jakarta or MicroProfile? Or do you need some help in getting up and running with your next project? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Enjoy

Categories
Atbash Jakarta EE Testing

Jakarta EE Integration Testing

Testing your application is very important but also difficult to do. Unit tests are relatively easy to create but it can be difficult to run them with realistic values. So developers sometimes use unrealistic values and constructs just to have the code coverage above the company-required threshold.

Integration testing is even more difficult as you need additional systems, like a database, with a predefined set of data to have repeatable tests. So some test frameworks solve this problem by creating specific artefacts for the test, containing parts of your application and fake and mocked other parts. But you are no longer testing your end product anymore and merely are testing that specific test artefact.

With the availability of the Testcontainers framework, developers can again test the real application by deploying it in a container using the runtime that will be used in production. External dependencies like databases can also be set up easily within a container and initialised with the required dataset for each test. At last, we can actually perform some true integration testing.

Now that we have used it for some time, we see that we always use certain code snippets over and over again in our tests.

– Starting the container with the runtime and your application

– Calling the endpoints of our web application and verifying the structure and response codes

– Setting up remote debugging of our application to find out what is going wrong in our tests.

And so on.

The idea of the Atbash Integration testing framework for Jakarta EE is to provide all those snippets in a reusable dependency so that we can quickly and in a uniform way create integration tests for our Jakarta EE applications. As the focus these days of applications are micro services or at least applications that provide REST endpoints, the focus of the testing framework is also for these applications.

Pieces of the Puzzle

The framework is built as a JUnit 5 extension that brings together all the pieces so that you as a developer can very easily start testing your application. Testing a ‘Hello World’ application is as simple as the Hello World application itself.

@ContainerIntegrationTest(runtime = SupportedRuntime.PAYARA_MICRO)
public class HelloPayaraIT extends AbstractContainerIntegrationTest {


  @RestClient
  public HelloService helloService;

  @Test
  void sayHello() {
    String value = helloService.sayHello("JUnit");
    Assertions.assertThat(value).isEqualTo("Hello JUnit");
  }
}

The test starts the container with Payara Micro, deploys your application, and calls the ‘Hello World’ endpoint to verify if the response is as expected. As already mentioned, the Testcontainers framework is used and with the help of Apache CXF, Jackson, and the MicroProfile Rest Client specification, endpoints can be called very easily by defining an interface with the contract of your endpoint(s).

Deploy the Application

You don’t need to do anything. The extension looks for the war file in the target directory and makes sure it ends up in the Docker image that is created on the fly.

In future versions, the search logic will be made more flexible so that it can be used when you don’t use the default structure or Gradle for example.

Define the Runtime

The framework supports Payara Micro, OpenLiberty, and WildFly as Jakarta EE runtime. You can also use Glassfish but there are several limitations with it. See more on that later on.

The runtime that the framework will use, is determined as the value of the ‘runtime’ member of the @ContainerIntegrationTest annotation. You can also live this value to its default and specify the runtime through the JVM System Properties be.atbash.test.runtime. This is especially handy if you are developing a Jakarta EE library or framework and want to test it out on multiple runtimes easily.

The JUnit5 extension creates a specific Docker image for each runtime based on the official Docker image. The tag number of that base image is taken from a default (look at the documentation to find out which number it is) or you can specify it also as a JVM System property.

When this property specifies not only the tag number, but the entire Docker Image identification like payara/micro:5.2022.2 it uses it as the base image. In the example, we use the JDK8 version of Payara Micro 5.2022.2 instead of the default JDK11 version that is chosen by the framework.

The System property can also be used to select a Jakarta EE 9.x or even Jakarta EE 10 based version of the runtime and not the default Jakarta EE 8 ones. This is possible because for the integration tests we only call the application endpoints and there is no direct dependency of the test framework on the Jakarta version that is used within your application.

Also, Glassfish is supported but with a few limitations since there is no official Docker image available. This means I had to use the Docklands image from Adam Bien but it only has an image that is based on JDK8. This image lacks the possibility to have a remote debugging session. See more on that later on in the text.

Call the Endpoints

The second major point of the framework is the simple way how endpoints of your application can be called. The JVM itself can be used for this with the URL class and the openConnection() method for example. But it is rather cumbersome, especially if you do need to create the JSON and read the response, and especially can be tricky since the host and port where the application is running are determined by Testcontainers and the Docker Client.

Although Testcontainers is used under the hood, the developer that writes the test is, as little as possible, confronted with it. For that reason, I choose to make use of the MicroProfile Rest Client to call the application endpoints. The only thing we need to do is to define the ‘contract’ of the endpoint as an interface.

@Path("/hello")
public interface HelloService {

  @GET
  @Path("{name}")
  String sayHello(@PathParam("name") String name);
}

Based on this definition, Apache CXF can create a proxy that actually calls the endpoint of your application. The JUnit5 extension knows or can find out from Testcontainers the URL on which your application is accessible. And with the help of Jackson, you as a developer are also not confronted with creating and reading JSON content. Automatic conversion to Java instances is performed.

The proxy can be ‘injected’ into your test class by using the @RestClient annotation of MicroProfile.

Behind the scenes, some low-level handling and reflection are going on. To avoid the usage of setAccessible() method, the test class and the field where the rest Client is injected must be declared with the scope ‘public’. So that we can avoid warnings and problems by the JVM when performing this low-level stuff.

Some Other Features

Besides the main features described above to automatically deploy your application in a specially created Docker container using the runtime of your choice and making it easy to call endpoints of the application, there are a few other useful features.

Remote debugging

When your test fails, it is very handy that you can start a debugging session to follow the code execution to the point where it goes wrong. When performing integration tests, a remote debugging session will be the fastest and easiest way to find the problem. The integration testing framework has support for remote debugging with a simple option. Just set the debug member of the annotation to true.

@ContainerIntegrationTest(runtime = SupportedRuntime.PAYARA_MICRO, debug = true)

When the container starts, the JVM will wait due to the suspend=y option until the debugger is attached. So when you see the message that Testcontainers is waiting until the container is started, connect the debugger to localhost:5005 (or the host the container is accessible on, you see already some messages in the log where the container is). The start-up continues and comes to the point where you have placed a breakpoint in your application code.

Logging

First of all, make sure you configure a logging output framework like Logback as described in the Testcontainers documentation. The framework has two additional features to show the container log for a specific test.

When a test fails due to a java.lang.AssertionError error, an URL that could not be found (proxy received a status 404) or there was an internal server error detected, the entire container log is shown as part of the test output.

You also can specify the value liveLogging = true resulting in showing the container logs during the execution of the test. It appears as the runtime log is written out ‘immediately’ when generated by the runtime but there is a very small delay.

@ContainerIntegrationTest(runtime = SupportedRuntime.PAYARA_MICRO, liveLogging = true)

Planned Features

The current version of the framework (see Github and Maven Central) already allows to perform an integration test with the Jakarta runtimes but there are already some improvements in the pipeline to cover more use case scenarios.

– Support for database containers and defining the initial data set easily

– Support for integration with other (micro)service(s) that also start as a Docker container during the test.

Atbash Training and Support

Do you need a specific training session on integration testing with a Jakarta runtime or want some help in setting up integration tests? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Enjoy

Categories
Testing

Programmatic CDI access and Unit testing

Introduction

There are situations where you need to access the CDI system programmatically, instead of using annotations like @Inject

  • You want to retrieve a CDI bean within a context (class) which isn’t CDI-aware.
  • It is easier to retrieve an optional or multiple beans programmatically instead of using Instance.

Since CDI 1.1 (Java EE 6), this can be easily achieved by using

CDI.current();

The programmatic access is most useful if you are creating a library/framework or some reusable piece of functionality. But I guess everyone who creates more than 1 application is in the situation where things can be reused.

This blog shows the equivalent programmatic constructs but most importantly, how you can unit test your bean in those cases (no CDI container needed!)

Optional CDI bean

An optional bean is interesting in the situation where you define some basic functionality but in case the developer defines a CDI bean implementing the interface, that custom developer logic is executed.

public interface SomeInterface {}
// with classic injection
@Inject
private Instance<SomeInterface> optional;

   // Within a method
   if (!optional.isUnsatisfied()) {
      optional.get();
   }
// programmatic
// Within method
Instance<SomeInterface> instance = CDI.current().select(SomeInterface.class);
instance.isUnsatisfied() ? null : instance.get();

Multiple beans

Within a Chain of Responsibility pattern or the situation where you can have multiple add-ons which perform some logic in a certain situation, are the ideal situations for having multiple CDI beans implementing an interface.

// with classic injection
@Inject
private Instance<SomeInterface> addonInstances;

   // Within a method
   List<SomeInterface> addons = new ArrayList<>();
   addonInstances.iterator().forEachRemaining(
      addons::add
   );

// programmatic
// Within a method
List<SomeInterface> addons = new ArrayList<>();
for (SomeInterface addon : CDI.current().select(SomeInterface.class)) {
   result.add(addon);
}

Sending Events

One of the nice features of CDI is the ability to sends events between consumers and producers without the need of registering the consumers with the producer for example.

Producing the Event can be done using

// with classic injection
@Inject
private Event<Payload> payloadEvent;

   // Within a method
   payloadEvent.fire(new Payload());
// programmatic
// within a method
CDI.current().getBeanManager().fireEvent(new Payload());

Unit Testing

Now that you have used some CDI bean retrievals in a programmatic fashion, what happens when you execute a unit test?

java.lang.IllegalStateException: Unable to access CDI

When running with plain Java, without any CDI container, you get this message because the API doesn’t contain an implementation, only the specification of the behavior.

You can of course switch to integration testing, where you launch a proper CDI container. But you can, of course, stay with your basic unit testing and have a simple, minimalistic Cdi container implementation (which is by the way not very difficult to create)

Suppose we have following code

public SomeInterface readOptionalInstance() {
   Instance<SomeInterface> instance = CDI.current().select(SomeInterface.class);
   return instance.isUnsatisfied() ? null : instance.get();
}

And we like to test this piece of code then the optional bean is not defined with something like this

@Test
public void readOptionalInstance_NotPresent() {
   Assert.assertNull(viewBean.readOptionalInstance());
}

We need a simple CDI implementation. This is provided by the be.atbash.util.BeanManagerFake class, available in the tests artifact of atbash utils-cdi.

<dependency>
   <groupId>be.atbash.utils</groupId>
   <artifactId>utils-cdi</artifactId>
   <version>0.9.0</version>
   <classifier>tests</classifier>
   <scope>test</scope>
</dependency>

<dependency>
   <groupId>org.mockito</groupId>
   <artifactId>mockito-core</artifactId>
   <version>2.13.0</version>
   <scope>test</scope>
</dependency>

The CDI fake container (as in not real implementation, maybe not the correct term in the test concepts Dummy/Fake/Stub/Spy) is using Mockito underneath, so that is also a dependency we need to add to the project.

The test can then be adapted as follow

private BeanManagerFake beanManagerFake = new BeanManagerFake();

@After
public void cleanup() {
   beanManagerFake.deregistration();
}

@Test
public void readOptionalInstance_NotPresent() {
   beanManagerFake.endRegistration();
   Assert.assertNull(viewBean.readOptionalInstance());
}

We can instantiate the BeanManagerFake as a regular bean. Important here is the deregistration method after the test is finished. It is the idea to keep the fake CDI container between tests alive but only clear the contents. In the current version, the container is also unset which makes it necessary that the beanManagerFake variable is an instance variable (and not a class variable)

With the endRegistration method, we signal that all configuration of the CDI container has taken place and that the operational modus can start. It can give some instances of interfaces if needed as in our test example.

Here we didn’t register anything, so we get a null back.

This is updated test class which contains also a test in the case we specify an instance in the CDI container.

@RunWith(MockitoJUnitRunner.class)
public class ViewBeanTest {

   private ViewBean viewBean = new ViewBean();

   @Mock
   private SomeInterface mockClass;

   private BeanManagerFake beanManagerFake = new BeanManagerFake();

   @After
   public void cleanup() {
      beanManagerFake.deregistration();
   }

   @Test
   public void readOptionalInstance_NotPresent() {
      beanManagerFake.endRegistration();
      Assert.assertNull(viewBean.readOptionalInstance());
   }

   @Test
   public void readOptionalInstance_Present() {
      beanManagerFake.registerBean(mockClass, SomeInterface.class);
      beanManagerFake.endRegistration();
      Assert.assertNotNull(viewBean.readOptionalInstance());
      Assert.assertTrue(viewBean.readOptionalInstance() == mockClass)
   }
}

You can see that we can define a mock (with the @Mock of Mockito) as a bean within the CDI container with the registered bean. We supply the instance and the type to which this instance must be bound. Within CDI, an instance can be bound to multiple class instances like the class itself, the interface, Object.class etc …) This is also possible with BeanManagerFake as the second parameter of the registerBean method is a varargs with all the class values.

And if you run the above test, you will see they are all green, so we, in fact, get the exact instance of the CDI bean (here the mock) back as the return value of the method.

Of course, when you have very complex Cdi constructions, it is better to test them in a real CDI container. But if the focus is on the business logic, the simple CDI container for testing will do.

Code

The code of BeanManagerFake is available in the Atbash utils GitHub repository and artifacts are on Maven Central.

Conclusion

Using the programmatic interfaces of the CDI container is easier in those cases where you need to retrieve optional or multiple CDI beans. And of course in the context where the dependency injection isn’t available. And with the help of the BeanManagerFake, a simple CDI container for testing, Unit tests are possible which make is easier then to set up a full blown integration test with a container.

Have fun.

This website uses cookies. By continuing to use this site, you accept our use of cookies.  Learn more