Continuous integration for CUBA apps with Jenkins


Hello everyone and welcome to our video devoted to Continuous integration for CUBA apps with Jenkins.

In this video, you will learn how to:

  • get started with Jenkins as an automation server for CUBA application
  • set up automated deployment, running unit and integration tests, code coverage and continuous delivery to test environments.
We will use the sample-sales application for demonstration. It can be downloaded from the Studio welcome screen in the Samples tab or from GitHub.

What is Jenkins

Jenkins is an open source automation tool written in Java with plugins built for Continuous Integration purpose. Jenkins triggers a build for every change made in the source code repository for example Git repository. Once the code is built it deploys it on the test server for testing. Finally, Jenkins deploys the build application on the production server.

Jenkins provides various native packages to streamline the installation to major operating systems. [This](https://jenkins.io/doc/book/installing/) page describes the procedures for new installations of Jenkins on a single/local machine. This content can also be used to help set up Jenkins in production environments.

Jenkins configuring

We assume that you already have Jenkins up and running and have administrator privileges. In this video, we are going to use Git as a project SCM. We need to install additional plugins that will enable us to execute Gradle tasks and to use the Git repository to pull the source code for building. To install plugins in Jenkins use the Manage Jenkins -> Manager Plugins link and search for the plugin you want to install. Select plugin from the list, install it and restart Jenkins. At the start, we add two plugins: Git Plugin and Gradle Plugin.

The build of a project is handled via jobs in Jenkins. So let's create a new job for our project.
Click the New Item. Then, enter a name for the job, select Freestyle Job and click OK.

Configure how the source code can be retrieved. We are using Git, so enter the URL to the Git repository. If the repository is not public, you may also need to configure the credentials.

In the Build section you can add a build step. Click the Add build step button, select Invoke Gradle plugin. Tick Use Gradle Wrapper. In the Tasks field add the commands you need to deploy the application. An our case it will be `clean, assemble`.

Click Save to finish the job definition. Click Build Now on the job page to validate the job works as expected.

After a while, the job should go to green or blue (depending on your configuration), if successful. Click on the job and afterwards on Console Output to see the log file. Here you can analyze the build errors.

Running tests on CI server

Now we will show how to configure Gradle and Jenkins for automated JUnit testing and reporting.

How to run the tests

Before we start looking at test running, we need to have a code sample.

Unit tests can be created and run both at the Middleware and the Client tiers.

The platform test modules bring the dependencies on JUnit and JMockit libraries out-of-the-box. But you can use any third party Java libraries for test automation.

Let's see how this works. Go to the IDE. Open order-edit.xml file. Switch to the screen controller.

Here we have created the OrderHelperService bean that is used to calculate the order amount. Let's check the code.

So, it's pretty simple method, which iterates through all the order's lines and sums up the multiplication of the product price by its quantity.

Let's create a unit test for this bean:


package com.company.sales;

import com.company.sales.entity.Order;
import com.company.sales.entity.OrderLine;
import com.company.sales.entity.Product;
import com.company.sales.service.OrderHelperServiceBean;
import org.junit.Before;
import org.junit.Test;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;

import static org.junit.Assert.assertEquals;

public class OrderServiceTest {

    private OrderHelperServiceBean orderService;
    private Order order;

    @Before
    public void setUp() throws Exception {
        orderService = new OrderHelperServiceBean();

        order = new Order();
        order.setDate(new Date());

        Product product = new Product();
        product.setName("Pen");
        product.setPrice(BigDecimal.TEN);

        OrderLine orderLine = new OrderLine();
        orderLine.setProduct(product);
        orderLine.setQuantity(BigDecimal.valueOf(2));
        orderLine.setOrder(order);

        order.setLines(Collections.singletonList(orderLine));
    }

    @Test
    public void testCalculateAmount() {
        BigDecimal amount = orderService.calculateAmount(order, order.getLines());
        order.setAmount(amount);
        assertEquals(BigDecimal.valueOf(20), amount);
    }

}

We recommend using a separate test database. So, let's create gradle task for the test database creation. Open build.gradle file. Within configure(coreModule) add the following


configure(coreModule) {
...
    task createTestDb(dependsOn: assemble, description: 'Creates local Postgres database for tests', type: CubaDbCreation) {
        dbms = 'postgres'
        dbName = 'sales_test'
        dbUser = 'cuba'
        dbPassword = 'cuba'
    }

To run the tests in Jenkins, select the project. Click Configure. In the Build section add the following commands to the Tasks field:


`createTestDb test`

The test task is the standard task of Java Plugin for Gradle. Click Save. Then trigger a new build by clicking on Project -> Build now. Then click on the Build Process, and then on the Console output.

Display JUnit Test Result Reports

Now we will show how to add the JUnit test reports to the Jenkins build process.
For Jenkins JUnit reporting, we need to install the JUnit Plug-in. You can install the plugin without reloading Jenkins.

In this step, we will configure Jenkins, so it will display the test results for individual builds as well as trend reporting. For that, navigate to Jenkins and choose the project. Click Configure. Add Post-build Actions. Select Publish JUnit test results report from the drop-down list. Add the following to the Test report XMLs field, since this is the path, where Gradle is placing its JUnit test result reports:

**/build/test-results/test/TEST-*.xml

Click Save to finish the job definition. Click Build Now on the job page to validate the job works as expected. Wait for the job to be executed.

On the Status page, you see if there were any failed tests. When you click on the Tests Result link on the left (or on the lower middle part on the Status page), you will see more details. Here we can see that we have had four tests and 100% of them were successful.

Code coverage

Code coverage is a metric that can help you understand how much of your source is tested. It's a very useful metric that can help you assess the quality of your test suite, and we will see here how you can get started with your projects.

First, we need to install the JaCoCo Plug-in. This plugin allows you to capture code coverage report from JaCoCo. Jenkins will generate the trend report of coverage. The plugin provides two things, a build-publisher to record and display coverage data as part of builds as well as a new column-type for dashboard views which can display coverage data in Dashboards.

In order to get the coverage data published to Jenkins, you need to add a JaCoCo publisher and configure it so it will find all the necessary information. Use the help provided via the question-mark links for more information. Basically, you specify where the *.exec files are:


**/build/jacoco/**.exec

where compiled code can be found:


**/classes,**/enhanced-classes,**/core/build/classes
and where the corresponding source code is located after the build is finished to let the plugin gather all necessary pieces of information:

**/src/
Also you need to exclude all Tests, Entities and Screens classes. Write the following to the Exclusions field:

**/classes/java/main/com/company/sales/entity/**,**/*Test*.class,**/com/company/sales/web/**

Click Save to finish the job definition. Click Build Now on the job page to validate the job works as expected.

After the job executed, the build-output will show that the JaCoCo-publisher is executed and collects the data. This output can also give hints if something goes wrong at this stage.

If data gathering is successful, the build will include a link to the coverage results similar to the HTML report of JaCoCo itself. The job page will be enhanced with a chart with the trend of code coverage over the last builds.

Let's have a closer look at our service coverage.In the Coverage Breakdown by Package select the `com.company.sales.service` package. Click on it. In the newly open window select OrderHelperServiceBean in the Coverage Breakdown by Source File section.

Our report shows 100% instructions coverage, 100% branches coverage and so on. As you can see all lines/branches/paths in our code are fully covered.
Keep in mind though, 100% code coverage does not necessary reflects effective testing, as it only reflects the amount of code exercised during tests.

Continuous deployment to tests environments

Let's assume that we have to prepare the application for the testing and release. For this purpose, we need to deploy an application into the production environment. Continuous deployment approach allows as to achieve this goal in Jenkins.

The following steps describe how to configure the app and the job in Jenkins.

App configuring

First, we need to set up the application task to produce a war file:


task buildWar(type: CubaWarBuilding) {
    coreContextXmlPath = 'modules/core/web/META-INF/war-context.xml'
    webXmlPath = 'modules/web/web/WEB-INF/single-war-web.xml'
    appProperties = ['cuba.automaticDatabaseUpdate' : true]
    includeContextXml = true
    includeJdbcDriver = true
    appHome = '${catalina.base}/work/app_home'
}

This is the task of the CubaWarBuilding type, which builds a WAR file from the application code and its dependencies. It should be declared in the root of `build.gradle`. The resulting WAR file is located in the build/distributions project subdirectory.

Pay attention to the `appHome` parameter - this is the path to the application home directory. In our case, it depends on the `CATALINA_BASE` environment variable, which defines where the running configuration of Tomcat exists.

Tomcat configuring

In many production environments, it is very useful to have the capability to deploy a new web application or undeploy an existing one, without having to shut down and restart the entire container.

To support these capabilities, Tomcat includes a web application (installed on context path /manager). Note, that a default Tomcat installation in Ubuntu does not include the Manager, so you have to install it manually.

Also, in order to enable remote deployments in Tomcat, you have to add a user with the role `manager-script`. To do so, edit the file `../conf/tomcat-users.xml` and add the following line:


<user username="deployer" password="deployer" roles="manager-script"> 

The last thing you have to specified is he maximum size, in bytes, of the memory allocation pool for a JVM. To do so you have to create `setenv.sh` (for Linux/Mac) or `setenv.bat` (for windows) file under /bin directory with below parameters:


export CATALINA_OPTS="$CATALINA_OPTS -Xmx512m"

At last, run the Tomcat server.

Jenkins configuring

Now, let's see how to extend a Jenkins job to automatically deploy a built war file to a Tomcat instance.
First of all, select Manage Jenkins followed by Manage Plugins. Select the Available tab, locate the Deploy to container plugin and install it. Then, go to your job and select Configure. In the Build section add the following commands to the Tasks field:

`buildWar`

Next, scroll down to the bottom of the page to the Post-build Actions. Select the option Deploy war/ear to a container from the Add post-build action dropdown button. Fill in the new fields.

In the WAR/EAR files field add the path to the WAR file in the project, for the CUBA applications the default way is the following:


**/build/distributions/war/*.war

The context path is the context path part of the URL under which your application will be published in Tomcat, for the CUBA apps this is the module prefix, `app` by default.
Select the appropriate Tomcat version from the Container dropdown box.
For the manager’s user name and password just copy over what you’ve entered in the tomcat-users.xml file. Select the newly created credentials from the drop down list.
The Tomcat URL is the base URL through which your Tomcat instance can be reached.
Finally, don’t forget to save the configuration.

Schedule a build for your job in Jenkins. If you check out the log file you should see one or more lines near the end indicating that the war file has been deployed. If you look in the log files in Tomcat (catalina.out) you should also see that your application has been successfully deployed.

When the job is complete, you should be able to open your freshly deployed application at the URL and context path you’ve specified in the job configuration in Jenkins .

If you have any questions, feel free to post questions on our forum page, and be sure you’ll soon get an answer.