Technology environment

CI is critical in projects where safety and quality are the main drivers.

So I started with a “proof of concept” with my team to show that the following technologies are ready to work together:

  • Java 8, NetBeans 8.0 & Ant
  • JUnit 4 & Jacoco 0.7.1
  • Jenkins & Sonar 4.2

The scope of this article is to explain all the steps needed to install and set up the tools necessary to make Java 8’s CI server fully functional. Note that this proof has been done on the Windows 7 developer machine, but is easy to do. The same is true in Linux servers.

The following figure shows at a high level the architecture that will be described in this post.

Java 8 & NetBeans 8.0 & Ant

We are creating a modular application. The application has a multi-tier architecture, where each layer is a suite of modules, and the final executable is just a set of integration suites.

We are using Ant to build our project, but if you are using Maven, you can even simplify the process because the Sonar integration in Jenkins can be done using Maven plug-ins.

JUnit 4 & Jacoco 0.7.1

Naturally, we are unit testing, so we use JUnit4. It integrates well anywhere, especially in NetBeans.

Jacoco is an excellent tool for generating code coverage, and since version 0.7.1, it fully supports Java 8.

Jenkins & Sonar 4.2

Jenkins is the engine for our CI server and it will be integrated with all of the above technologies without any problems. The version tested was 1.554.

Sonar is running all quality analysis on the code. Version 4.2 is fully compatible with Java 8.

Using Sonar with Ant requires a small library with goals to integrate into Jenkins. If you are using Maven, you can just install the Maven plug-in.

Project configuration

1. Install Java 8

2. Create a module suite with several modules, classes, and jUnit tests

3. Submit the code to your source code versioning server

4. Create a folder in the wire harness named “Jacoco-0.7.1” that contains the downloaded Jacoco jars

5, create a folder in the wire harness called “Sonar ant-task” and place it in the downloaded sonar folder

6, create a file in a tool named Sonar -jacoco-module.xml and paste the following code into it:

<? The XML version = "1.0" encoding = "utf-8"? > <! -- --> <project name="sonar-jacoco-module" basedir="." xmlns:jacoco="antlib:org.jacoco.ant" xmlns:sonar="antlib:org.sonar.ant"> <description>Builds the module suite otherSuite.</description> <property Name = "jacoco. Dir" location = "${nbplatform. Default. The harness. Dir} / jacoco - 0.7.1" / > < property name = "result. The exec. The file" location="${jacoco.dir}/jacoco.exec"/> <property name="build.test.results.dir" location="build/test/unit/results"/> <property file="nbproject/project.properties"/> <! -- Step 1: Import JaCoCo Ant tasks --> <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml"> <classpath path="${jacoco.dir}/jacocoant.jar"/> </taskdef> <! -- Target at the level of modules --> <target name="-do-junit" depends="test-init"> <echo message="Doing testing for jacoco"/> <macrodef name="junit-impl"> <attribute name="test.type"/> <attribute name="disable.apple.ui" default="false"/> <sequential> <jacoco:coverage destfile="${build.test.results.dir}/${code.name.base}_jacoco.exec"> <junit showoutput="true" fork="true" failureproperty="tests.failed" errorproperty="tests.failed" filtertrace="${test.filter.trace}" tempdir="${build.test.@{test.type}.results.dir}" timeout="${test.timeout}"> <batchtest todir="${build.test.@{test.type}.results.dir}"> <fileset dir="${build.test.@{test.type}.classes.dir}" includes="${test.includes}" excludes="${test.excludes}"/> </batchtest> <classpath refid="test.@{test.type}.run.cp"/> <syspropertyset refid="test.@{test.type}.properties"/> <jvmarg value="${test.bootclasspath.prepend.args}"/> <jvmarg line="${test.run.args}"/> <! --needed to have tests NOT to steal focus when running, works in latest apple jdk update only.--> <sysproperty key="apple.awt.UIElement" value="@{disable.apple.ui}"/> <formatter type="brief" usefile="false"/> <formatter type="xml"/> </junit> </jacoco:coverage> <copy file="${build.test.results.dir}/${code.name.base}_jacoco.exec" todir="${suite.dir}/build/coverage"/> <! -- Copy the result of all the unit tests of all the modules into one common folder at the level of the suite, so that sonar could find those files to generate associated reports --> <copy todir="${suite.dir}/build/test-results"> <fileset dir="${build.test.results.dir}"> <include name="**/TEST*.xml"/> </fileset> </copy> <fail if="tests.failed" unless="continue.after.failing.tests">Some tests failed; see details above. </fail> </sequential> </macrodef> <junit-impl test.type="${run.test.type}" disable.apple.ui="${disable.apple.ui}"/> </target> </project>Copy the code

The scope of this file is to cover the do-junit task of adding jacoco coverage and copy the unit test results for each module in the suite build so that sonar will find all these elements together for analysis.

7, create a file in the wire harness named Sonar -jacoco-suite.xml and paste the following code into it

<? The XML version = "1.0" encoding = "utf-8"? > <project name="sonar-jacoco-suite" basedir="." xmlns:jacoco="antlib:org.jacoco.ant" xmlns:sonar="antlib:org.sonar.ant"> <description>Builds the module suite otherSuite.</description> <property Name = "jacoco. Dir" location = "${nbplatform. Default. The harness. Dir} / jacoco - 0.7.1" / > < property name = "result. The exec. The file" location="build/coverage"/> <! -- Define the SonarQube global properties (the most usual way is to pass these properties via the command line) --> <property name="sonar.jdbc.url" value="jdbc:mysql://localhost:3306/sonar? useUnicode=true&characterEncoding=utf8" /> <property name="sonar.jdbc.username" value="sonar" /> <property name="sonar.jdbc.password" value="sonar" /> <! -- Define the SonarQube project properties --> <property name="sonar.projectKey" value="org.codehaus.sonar:example-java-ant" /> <property name="sonar.projectName" value="Simple Java Project analyzed With the SonarQube Ant Task" /> <property name="sonar. ProjectVersion "value="1.0" /> <property name="sonar.language" value="java" /> <! -- Load the project properties file for retrieving the modules of the suite --> <property file="nbproject/project.properties"/> <! -- Using Javascript functions to build the paths of the data source for sonar configuration --> <script language="javascript"> <! [CDATA[ // getting the value modulesName = project.getProperty("modules"); modulesName = modulesName.replace(":",","); res = modulesName.split(","); srcModules = ""; binariesModules = ""; testModules = ""; //Build the paths for (var i=0; i<res.length; i++) { srcModules += res[i]+"/src,"; binariesModules += res[i]+"/build/classes,"; testModules += res[i]+"/test,"; } //Remove the last comma srcModules = srcModules.substring(0, srcModules.length - 1); binariesModules = binariesModules.substring(0, binariesModules.length - 1); testModules = testModules.substring(0, testModules.length - 1); // store the result in a new properties project.setProperty("srcModulesPath",srcModules); project.setProperty("binariesModulesPath",binariesModules); project.setProperty("testModulesPath",testModules); ]]> </script> <! -- Display the values --> <property name="sonar.sources" value="${srcModulesPath}"/> <property name="sonar.binaries" value="${binariesModulesPath}" /> <property name="sonar.tests" value="${testModulesPath}" /> <! -- Define where the coverage reports are located --> <! -- Tells SonarQube to reuse existing reports for unit tests execution and coverage reports --> <property name="sonar.dynamicAnalysis" value="reuseReports" /> <! -- Tells SonarQube where the unit tests execution reports are --> <property name="sonar.junit.reportsPath" value="build/test-results" /> <! -- Tells SonarQube that the code coverage tool by unit tests is JaCoCo --> <property name="sonar.java.coveragePlugin" value="jacoco" /> <! -- Tells SonarQube where the unit tests code coverage report is --> <property name="sonar.jacoco.reportPath" value="${result.exec.file}/merged.exec" /> <! -- Step 1: Import JaCoCo Ant tasks --> <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml"> <classpath path="${jacoco.dir}/jacocoant.jar"/> </taskdef> <target name="merge-coverage"> <jacoco:merge destfile="${result.exec.file}/merged.exec"> <fileset dir="${result.exec.file}" includes="*.exec"/> </jacoco:merge> </target> <target name="sonar"> <taskdef uri="antlib:org.sonar.ant" resource="org/sonar/ant/antlib.xml"> <! -- Update the following line, or put the "sonar-ant-task-*.jar" file in your "$HOME/.ant/lib" folder --> <classpath Path = "${harness. Dir} / sonar - ant - task - 2.1 / sonar - ant task - 2.1. The jar" / > < / taskdef > <! -- Execute the SonarQube analysis --> <sonar:sonar /> </target> </project>Copy the code

The scope of this file is to define sonar configurations and sonar tasks at the suite level. If you use sonar, some special database or special user will have to change the configuration here.

Another task defined is the Jacoco merge, which will actually take all the generated exec for each module and merge them into a single exec in the suite build to allow sonar to analyze.

8. Replace the build.xml content for each module with the following:

<description>Builds, tests, and runs the project com.infrabel.jacoco.</description>
<property file="nbproject/suite.properties"/>
<property file="${suite.dir}/nbproject/private/platform-private.properties"/>
<property file="${user.properties.file}"/>
<import file="${nbplatform.default.harness.dir}/sonar-jacoco-module.xml"/>
<import file="nbproject/build-impl.xml"/>Copy the code

9. Replace the build.xml content for each suite with the following:

<description>Builds the module suite otherSuite.</description>
<property file="nbproject/private/platform-private.properties"/>
<property file="${user.properties.file}"/>
<import file="${nbplatform.default.harness.dir}/sonar-jacoco-suite.xml"/>
<import file="nbproject/build-impl.xml"/>Copy the code

Jenkins

In “Manage Jenkins-> Manage Plug-ins”, go to the list of available plug-ins and install (if they don’t already exist) the following plug-ins:

  • JaCoCo
  • Mercurial or Subversion
  • Sonar

If you are behind a firewall or proxy and have problems configuring your network Settings, you can always manually download and install them from here. In this case, remember to download the dependencies for each plug-in first.

In manage Jenkins-> Configure System, check that all plug-ins are installed correctly, see the following screen capture for an example (replace folders with folders that fit you) :

Create a new free Style project, configure versioning for your preferences, and then add the following three “Invoce Ant” tasks in the Build panel:

Add a new Record Jacoco Coverage Report to the Post-build Operations panel with the following configuration:

Sonar

GRANT ALL PRIVILEGES ON ‘sonar’.* TO ‘sonar’@’localhost’;

Go to the sonar configuration file (sonar. Properties) in the conf folder of the installation and enable MySQL:

sonar.jdbc.username=sonar sonar.jdbc.password=sonar sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar? useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=trueCopy the code

In sonar configuration, update the Java plug-in if Java 8 compatibility is required

Selection of technical articles

  • Java prints the heart in one line of code
  • Linux performance monitoring software NetData Chinese version
  • Interface test Code Coverage (JACOCO) schema sharing
  • Performance testing framework
  • How to Perform a Pleasant Performance Test on the Linux Command-line Interface
  • Diagram HTTP brain map
  • Automatically turn the Swagger document into test code
  • Five lines of code build a static blog
  • A Test framework for linear interface based on Java
  • Practice in JUnit for Selenium testing

Selection of non-technical articles

  • Why choose software testing as a career path?
  • Programming thinking for everyone
  • 7 Steps to Becoming a Good Test Automation Engineer
  • An important reason manual tests exist
  • 7 skills to Automate tests
  • Automated and manual testing, balance!
  • Automate the test lifecycle
  • How do I introduce automated tests into DevOps