Changed my opinion about automated testing of OSB services.(see last article)
In stead of focussing on linking the input to the output, I am making the assumption that I am the only one testing on the DEV environment. Fair and reasonable assumption and fairly easy to implement…..
The assumption:
The development environment is using the Continuous Integration strategy. After each build and deployment automated testing will be done on the DEV environment.
My automated Junit/ XMLUnit example is executing the following steps:
- Posts a valid input XML file to a TestFacade
- The Facade writes the input (from the body) to a directory
- Test does a Thread.sleep (giving the OSB Interface the time to do it’s thing)
- OSB
- Proxy Service reads (with JCA File Adapter) an XML file
- To Queue via Business Service
- Proxy Service is doing VETRO stuff to the message
- Business Service is writing to FTP server
- Test/ Thread awakens and picks up the file
- Actual output file is validated against XML schema using xmlunit
- Actual output file is validated using xmlunit
The following technologies are being used: maven/ xmunit (for schema and xpath validations/ junit/ java/ apache-commons (for the FTP client)/ apache-httpclient (for the http post)
Maven pom.xml file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- BASIC -->
<groupId>com.datalinks</groupId>
<artifactId>testxmlvalidtion</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<scope>compile</scope>
</dependency>
</dependencies>
<!-- /BASIC -->
<!-- BUILD -->
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<!-- /BUILD -->
</project>
Later you can add a task to bamboo that will automatically checkout the test project and execute the following maven tasks…clean and test
But of course you can also manually execute the test in a shell environment…
The -o ensures that you use the local repository (make sure that you have the dependencies in your local respository then) In order to run this you need to have at least the following installed: JRE (also set the JAVA_HOME setting), installation of maven 2x or higher (also make sure you have the MVN_HOME or M2_HOME environment variable set to the path of your maven installation)….in your maven installation directory you will have a settings.xml file. I use this file to set the directory for the local maven repository, I do this with the following property
localRepository>/path/to/local/repo</localRepository>
Your Java project should contain the following directory structure
PROJECTDIR
---pom.xml (file)
---src
------main
---------java
------------[package directories]
------test
---------java
------------[package directories]
When you do a mvn test or package (not using the -Dmaven.test.skip=true option) maven will know that it needs to execute the tests that are in the src/test/java directory (and it sub directories of course)
Assuming you use eclipse (OEPE) as your IDE, and you have the M2_REPO variable set to the path of your local repository…you can create the project structure (and set the proper classpath for your project) by typing
TestFacade
Create a proxyService that has Service Type: Messaging Service and input TEXT, this service can do a callout which persist the body to a preconfigured directory (my implementation uses a parameter in the header, and based on the parameter SERVICE_NAME, it persists files in different directories…using the OSB Routing table). In stead of using JCA I prefer to do a simple JAVA call for writing the file:
package com.datalinks.testexample;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import org.xmlsoap.schemas.soap.envelope.impl.BodyImpl;
public class CustomFileWriter {
public static void writeFile(String fileName, String directory, BodyImpl content) throws IOException{
String tmpString = content.stringValue(); tmpString = tmpString.replaceFirst("<xml-fragment>", "");
tmpString = tmpString.replaceFirst("</xml-fragment>", "");
FileWriter fstream = new FileWriter(directory+fileName);
BufferedWriter out = new BufferedWriter(fstream);
out.write(tmpString);
out.close();
}
}
the java callout has 3 parameters: filename, directory and the OSB $body variable…which in java code is equal to the org.xmlsoap.schemas.soap.envelope.impl.BodyImpl object.
Also notice that I am removing the <xml-fragment> tags….this is added by OSB and changes the content of the XML file…so it needs to go.
Java code that is able to post HTTP call (with body)
package com.datalinks.testexample;
import java.io.*;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
public class HttpClientTester {
static Logger logger = Logger.getLogger(HttpClientTester.class);
private static String testFacadeUrl="http://localhost:7001/TestServices/ProxyServices/TestServicesFacade_PS?SERVICE_NAME=";
public static String callService(String serviceName) throws Exception {
String result = null;
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(testFacadeUrl+serviceName);
HttpResponse response = null;
response = httpclient.execute(httppost);
if(response.getStatusLine().getStatusCode()!=200){
throw new Exception("Exception calling "+serviceName+" statusCode "+response.getStatusLine().getStatusCode());
}
HttpEntity entity = response.getEntity();
if (entity != null) {
long len = entity.getContentLength();
if (len != -1 && len < 2048) {
result = EntityUtils.toString(entity);
}
}
return stripClobMessageFromHeaderAndFooter(result);
}
public static String callService(String serviceName, String body) throws Exception {
String result = null;
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(testFacadeUrl+serviceName);
StringEntity entityIn = new StringEntity(body);
httppost.setEntity(entityIn);
HttpResponse response = null;
response = httpclient.execute(httppost);
if(response.getStatusLine().getStatusCode()!=200){
throw new Exception("Exception calling "+serviceName+" statusCode "+response.getStatusLine().getStatusCode());
}
HttpEntity entity = response.getEntity()
if (entity != null) {
long len = entity.getContentLength();
if (len != -1 && len < 2048) {
result = EntityUtils.toString(entity);
}
return stripMessageFromHeaderAndFooter(result);
}
}
}