Automated testing using JUnit, Maven and Jenkins.
Preface
In my earlier post we went about the task of using Apache Tomcat as the application server that had Jenkins running in it as the Continuous Integration and Continuous Deployment application and Git as the source code repository. It also did the approval and checked for inline tasks that were completed and approved before deployment. One piece of missing cog in this is the automated testing application that we cans use to do Test Driven Development. Lets take a stab at that in this post.
Maven for Eclipse
For this I used Maven as the Project management application ans JUnit as the testing application tool. Maven can be installed as an Eclipse plugin. To do so click on the ‘Help’ and then ‘Install New Software’ button as shown below.
I am using the IBM Integration Bus toolkit at version 10 and found that the closest Maven plugin would be http://download.eclipse.org/technology/m2e/releases/1.4 . Add this to the list.
Select the first plugin from the list as shown below.
After the software has been installed in eclipse just to be safe restart eclipse.
Creating a Maven project in Eclipse
To create a maven project for testing.
Select the Maven Project option.
Create a simple project
Give it a Group Id and a Artifact Id and click on the button finish.
All done the project structure should look like this. The pom.xml contains the dependencies for the project. The test cases will be create in the src->test->java folder and the surefire reports will be created in the target folder.
Java API to run the JUnit test cases
Before we move any forward lets take a pause and understand what we have done till now. We installed the Maven plugin in our IIB toolkit and created a Maven project. To run the test cases, we need a java API that will write messages to queue, read messages from queue, send messages to http and also put a request message on a request queue and expect a response on the reply queue. I have added the Util class that has these methods and it can be downloaded from the this location. Import the project in eclipse and create a jar file as com.testing.mq.jar. We will be using this as one of the external libraries.
Jar files for Maven projects
Note:-This procedure is generally not recommended. It is better to have an external Maven repository that maintains the jar files. Some types of the free/open source repositories are JFrog’s artifactory or apache’s archiva. This page details the steps that will help you to import the jar files in a maven repository.
Coming back to our project, create a folder called ‘libs’ in the project and import the following libraries into this folder-
JUnit jar file can be downloaded from this location.
XMLUnit jar file can be downloaded from this location.
The other jar files needed in this case are available in the {WebSphereMQ Install Location}\java\lib folder.
Update the pom.xml file with the dependencies by adding the dependency block as following —
<?xml version="1.0"?>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<systemPath>${basedir}/libs/junit-4.12.jar</systemPath>
<scope>system</scope>
</dependency>
Test cases and test data
We have all the libraries ready, create 3 folders in the project and name them as ‘InputFile’, ‘ExpectedFile’ and ‘OutputFile’. As the name specifies each of them will contain the input file for the test, the expected output of the operation and the actual OutputFile.
In this instance there are 2 test cases, one is for the request containing just one stock quote request and the other is with multiple stock quote request.
The Next step would be to create the test case file in the folder src->test by right clicking the test folder and then selecting ‘New’ and ‘Other’. Select the ‘JUnit Test Case’
Following is a sample of the test case details. Please note the name is a general requirement to have it suffixed with ‘Test’.
Here is the JUnit class file -
package com.AggregateStockQuote;import java.io.IOException;import org.custommonkey.xmlunit.*;import org.junit.Test;import org.xml.sax.SAXException;import com.testing.mq.Util;public class StockSingleReqTest extends XMLTestCase {@Testpublic void test() throws IOException, InterruptedException, SAXException {// fail("Not yet implemented");String inputFile = System.getProperty("user.dir") + "/InputFile/StockQuoteReqSingle.xml";String outputFile = System.getProperty("user.dir") + "/OutputFile/StockQuoteRepSingle.xml";String controlFile = System.getProperty("user.dir") + "/ExpectedFile/StockQuoteRepSingle.xml";Util newUtil = new Util();newUtil.queueRequestReply("WQM.INT", "AGG.REQ", "AGG.REP", inputFile, outputFile, 8);String controlXML = newUtil.readFile(controlFile);String testXML = newUtil.readFile(outputFile);DifferenceListener myDifferenceListener = new IgnoreTextAndAttributeValuesDifferenceListener();Diff myDiff = new Diff(controlXML, testXML);myDiff.overrideDifferenceListener(myDifferenceListener);assertTrue("test XML matches control skeleton XML " + myDiff, myDiff.similar());// assertTrue("Comparing test xml to control xml", controlXML, testXML);}}
As the request and response is XML we are using xmlunit to ascertain if the request and response is similar. The different options available and an example of how to use it is available at this location.
Create a similar test for the Multi request and the folder should look like this.
Running the JUnit test case
Now that we have the artifacts done the next step is to run the test cases. To run the test cases, right click on the project and select the ‘Maven test’ as follows -
If the test run successfully the console will be populated with the verbose details — in this case we had the following output —
Results :
Failed tests: test(com.AggregateStockQuote.StockMultiReqTest): test XML matches control skeleton XML org.custommonkey.xmlunit.Diff
test(com.AggregateStockQuote.StockSingleReqTest): test XML matches control skeleton XML org.custommonkey.xmlunit.DiffTests run: 2, Failures: 2, Errors: 0, Skipped: 0[INFO] — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
[INFO] BUILD FAILURE
[INFO] — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
[INFO] Total time: 19.431s
[INFO] Finished at: Fri Dec 18 10:27:42 EST 2015
[INFO] Final Memory: 6M/10M
[INFO] — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.10:test (default-test) on project JUnit_AggregationStockQuote:
There are test failures.
The following artifacts are created in the target folder. The test-classes folder contain the complied class files of the junit test. The surefire-reports contains the results of the test that were run and they are in XML and TXT format.
The text reports contain the output of the test cases. If they have been success or failure -
SUCCESS- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Test set: com.AggregateStockQuote.StockSingleReqTest- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.957 secFAILURE- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Test set: com.AggregateStockQuote.StockSingleReqTest- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 7.957 sec <<< FAILURE!test(com.AggregateStockQuote.StockSingleReqTest) Time elapsed: 7.955 sec <<< FAILURE!junit.framework.AssertionFailedError: test XML matches control skeleton XML org.custommonkey.xmlunit.Diff[different] Expected number of child nodes '16' but was '1' - comparing <Stock…> at /StockQuotes[1]/Stock[1] to <Stock…> at /StockQuotes[1]/Stock[1]at junit.framework.Assert.fail(Assert.java:57)at junit.framework.Assert.assertTrue(Assert.java:22)at junit.framework.TestCase.assertTrue(TestCase.java:192)at com.AggregateStockQuote.StockSingleReqTest.test(StockSingleReqTest.java:36)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:95)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:56)at java.lang.reflect.Method.invoke(Method.java:620)at junit.framework.TestCase.runTest(TestCase.java:176)at junit.framework.TestCase.runBare(TestCase.java:141)at junit.framework.TestResult$1.protect(TestResult.java:122)at junit.framework.TestResult.runProtected(TestResult.java:142)at junit.framework.TestResult.run(TestResult.java:125)at junit.framework.TestCase.run(TestCase.java:129)at junit.framework.TestSuite.runTest(TestSuite.java:252)at junit.framework.TestSuite.run(TestSuite.java:247)at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:86)at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:53)at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:123)at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:104)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:95)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:56)at java.lang.reflect.Method.invoke(Method.java:620)at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:164)at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:110)at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:175)at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcessWhenForked(SurefireStarter.java:107)at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:68)
XML Surefire reports
Double click on one of the surefire report’s xml and
One can also run the test case from the JUnit perspective as shown above by right clicking on the test and selecting ‘Run’
Running JUnit from Jenkins
Now that we have accomplished the task of executing the Maven project in eclipse we can externalize the project by having it run from Jenkins. Create a project in Jenkins with the following details -
Save it and it is now available to run. Check on the console output after the project has finished running and it would appear something like this -
Console Output
Started by user intuser
Building in workspace C:\JenkinsHome\workspace\JUnit_MFA_AggregationStockQuote_MVN
Parsing POMs
Established TCP socket on 63238
[JUnit_AggregationStockQuote] $ C:\Java\jdk1.8.0_60/bin/java -cp C:\JenkinsHome\plugins\maven-plugin\WEB-INF\lib\maven32-agent-1.7.jar;D:\Build\apache-maven-3.3.3\boot\plexus-classworlds-2.5.2.jar;D:\Build\apache-maven-3.3.3/conf/logging jenkins.maven3.agent.Maven32Main D:\Build\apache-maven-3.3.3 D:\Build\apache-tomcat-8.0.22\webapps\jenkins\WEB-INF\lib\remoting-2.52.jar C:\JenkinsHome\plugins\maven-plugin\WEB-INF\lib\maven32-interceptor-1.7.jar C:\JenkinsHome\plugins\maven-plugin\WEB-INF\lib\maven3-interceptor-commons-1.7.jar 63238
<===[JENKINS REMOTING CAPACITY]===>channel started
Executing Maven: -B -f C:\Godfrey\git\IIBPoTs\JUnit_AggregationStockQuote\pom.xml install
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for JUnit_AggregationStockQuote:JUnit_AggregationStockQuote:jar:0.0.1-SNAPSHOT
[WARNING] 'dependencies.dependency.systemPath' for junit:junit:jar should not point at files within the project directory, ${basedir}/libs/junit-4.12.jar will be unresolvable by dependent projects @ line 22, column 16
[WARNING] 'dependencies.dependency.systemPath' for xmlunit:xmlunit:jar should not point at files within the project directory, ${basedir}/libs/xmlunit-1.4.jar will be unresolvable by dependent projects @ line 29, column 16
[WARNING] 'dependencies.dependency.systemPath' for com.ibm:com.ibm.mqjms:jar should not point at files within the project directory, ${basedir}/libs/com.ibm.mqjms.jar will be unresolvable by dependent projects @ line 39, column 16
[WARNING] 'dependencies.dependency.systemPath' for com.ibm:com.ibm.mq.headers:jar should not point at files within the project directory, ${basedir}/libs/com.ibm.mq.headers.jar will be unresolvable by dependent projects @ line 47, column 16
[WARNING] 'dependencies.dependency.systemPath' for com.ibm:connector:jar should not point at files within the project directory, ${basedir}/libs/connector.jar will be unresolvable by dependent projects @ line 55, column 16
[WARNING] 'dependencies.dependency.systemPath' for com.ibm:com.ibm.mq.jmqi:jar should not point at files within the project directory, ${basedir}/libs/com.ibm.mq.jmqi.jar will be unresolvable by dependent projects @ line 63, column 16
[WARNING] 'dependencies.dependency.systemPath' for com.ibm:com.ibm.mq.commonservices.jar:jar should not point at files within the project directory, ${basedir}/libs/com.ibm.mq.commonservices.jar will be unresolvable by dependent projects @ line 71, column 16
[WARNING] 'dependencies.dependency.systemPath' for testingmq:testingmq:jar should not point at files within the project directory, ${basedir}/libs/com.testing.mq.jar will be unresolvable by dependent projects @ line 79, column 16
[WARNING] 'dependencies.dependency.systemPath' for mqclasses:mqclasses:jar should not point at files within the project directory, ${basedir}/libs/com.ibm.mq.jar will be unresolvable by dependent projects @ line 86, column 16
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building JUnit_AggregationStockQuote 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ JUnit_AggregationStockQuote ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\Godfrey\git\IIBPoTs\JUnit_AggregationStockQuote\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ JUnit_AggregationStockQuote ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ JUnit_AggregationStockQuote ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\Godfrey\git\IIBPoTs\JUnit_AggregationStockQuote\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ JUnit_AggregationStockQuote ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to C:\Godfrey\git\IIBPoTs\JUnit_AggregationStockQuote\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ JUnit_AggregationStockQuote ---
[INFO] Surefire report directory: C:\Godfrey\git\IIBPoTs\JUnit_AggregationStockQuote\target\surefire-reports-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.AggregateStockQuote.StockMultiReqTest
MQJE001: Completion Code '2', Reason '2033'.
A WebSphere MQ Error occured : Completion Code 2 Reason Code 2033
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.721 sec
Running com.AggregateStockQuote.StockSingleReqTest
MQJE001: Completion Code '2', Reason '2033'.
A WebSphere MQ Error occured : Completion Code 2 Reason Code 2033
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.988 secResults :Tests run: 2, Failures: 0, Errors: 0, Skipped: 0[JENKINS] Recording test results
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ JUnit_AggregationStockQuote ---
[INFO] Building jar: C:\Godfrey\git\IIBPoTs\JUnit_AggregationStockQuote\target\JUnit_AggregationStockQuote-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ JUnit_AggregationStockQuote ---
[INFO] Installing C:\Godfrey\git\IIBPoTs\JUnit_AggregationStockQuote\target\JUnit_AggregationStockQuote-0.0.1-SNAPSHOT.jar to C:\Users\Godfrey\.m2\repository\JUnit_AggregationStockQuote\JUnit_AggregationStockQuote\0.0.1-SNAPSHOT\JUnit_AggregationStockQuote-0.0.1-SNAPSHOT.jar
[INFO] Installing C:\Godfrey\git\IIBPoTs\JUnit_AggregationStockQuote\pom.xml to C:\Users\Godfrey\.m2\repository\JUnit_AggregationStockQuote\JUnit_AggregationStockQuote\0.0.1-SNAPSHOT\JUnit_AggregationStockQuote-0.0.1-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 31.858 s
[INFO] Finished at: 2015-12-08T10:20:33-05:00
[INFO] Final Memory: 20M/202M
[INFO] ------------------------------------------------------------------------
Waiting for Jenkins to finish collecting data
[JENKINS] Archiving C:\Godfrey\git\IIBPoTs\JUnit_AggregationStockQuote\pom.xml to JUnit_AggregationStockQuote/JUnit_AggregationStockQuote/0.0.1-SNAPSHOT/JUnit_AggregationStockQuote-0.0.1-SNAPSHOT.pom
[JENKINS] Archiving C:\Godfrey\git\IIBPoTs\JUnit_AggregationStockQuote\target\JUnit_AggregationStockQuote-0.0.1-SNAPSHOT.jar to JUnit_AggregationStockQuote/JUnit_AggregationStockQuote/0.0.1-SNAPSHOT/JUnit_AggregationStockQuote-0.0.1-SNAPSHOT.jar
channel stopped
Finished: SUCCESS
The JUnit code used for this blog post can be found here.