Tuesday, November 8, 2016

A new method to Integrate SoapUI test results in Yandex Allure Dashboard

Here is a quick but effective solution for embedding SoapUI into Yandex Allure. I know, SoapUI supports Maven, but the SmarBear guys are getting too restrictive in recent days. They are trying their best to enforce all users to go for Pro versions which is now all integrated into Ready!API package.

I tried to add Allure Junit listeners into SoapUI pom files, but wasn't proudly successful.  Then decided to make it work in my own way. This way works smoother and is not so sensitive to environment or version changes.

What we need:
  • Having a local or remote database server, compatible with jdbc
  • Add the connector jar to SoapUI ext folder. For Ready!API, I had to directly copy the mysql connector jar in /lib folder 
  • In your Java/TestNG code, call SoapUI project test case using cmd and testrunner. You might find that assertion errors cause the process runner to wait till times out. To prevent this, you should pull a trick out of your sleeve: "cmd /B start /WAIT cmd /C" . This makes the process to immediately stop if it fails with stderr messages. 
  • In addition, you need to assign an unique identifier to your testrunner process so you can extract the results using that identifier easily. Here is the function I wrote:

        @Step("Running SoapUI Process")
 public boolean processrun(String testRunnerPath, String suiteName, String testCaseName, String uuid, String project){
  boolean finished = false;
   String pString = "cmd /B start /WAIT cmd.exe /C \""+testRunnerPath+" -s"+suiteName+" -c"+testCaseName+" -r -Puuid="+uuid+" "+project+"\"";
        String line;
        Process p = Runtime.getRuntime().exec(pString);
        BufferedReader input =  new BufferedReader(new InputStreamReader(p.getInputStream()));
        while ((line = input.readLine()) != null) {
         if (line.contains("TestCaseRunner Summary")){
          finished = true;
        if (finished) return true; else  return false;
  }catch (Exception e){
   if (finished) return true;
   else {
   System.out.println("--------------->Error Occured!");
   saveTextLog("Unexpected-Error", e.getMessage());
   Assert.fail("Unexpected Test Execution Error");
   return false;

  • Please note that above function looks for "TestCaseRunner Summary" string, which is always generated when you use "-r" flag. 
  • The uuid is a unique identifier string. You can simply generate it like "uuid = java.util.UUID.randomUUID()"
  • Write some groovy code in TearDown pane and record the first assertion failure in your table including the uuid. Here is how to get the step name, status, assertion errors:

 * Recording the results from each test case into MySQl
 * Author: ppirooznia
import com.eviware.soapui.impl.wsdl.teststeps.*
boolean next = true
import groovy.sql.*;
import java.util.zip.GZIPOutputStream
def driver = 'com.mysql.jdbc.Driver'
def con = Sql.newInstance("jdbc:mysql://","username","passsword",driver);
def uuid = context.expand( '${#Project#uuid}' )
log.info( testRunner.testCase.testSuite.name)// + testCase.testSuite.project.name )
assertion = ""
message = ""
request = ""
response = ""
boolean comma = false
boolean getinfo = false
lquery = ""
testsuitename = testRunner.testCase.testSuite.name
testcasename = testRunner.testCase.name
//testRunner.testCase.testSteps.each{ name,props ->

testRunner.testCase.testSuite.project.testSuites[testsuitename].getTestCaseByName(testcasename).testSteps.each{ name,props ->
step = testRunner.testCase.testSuite.project.testSuites[testsuitename].getTestCaseByName(testcasename).getTestStepByName("$name")
if (step instanceof WsdlTestRequestStep || step instanceof RestTestRequestStep || step instanceof JdbcRequestTestStep || step instanceof HttpTestRequestStep)
        log.info "$it.label - $it.status - $it.errors - $uuid"
  tname = "$name"
        if ("$it.status"=="FAILED") {
         getinfo = true
         if (comma){
          assertion += ", $it.label"
          message += ", $it.errors"
         } else {
          assertion += "$it.label" 
          message += "$it.errors"
          comma = true

        if (next && getinfo) {
          rawResponse = context.expand( '${'+"$name"+'#RawResponse}' )
           zResponse = zip(rawResponse)
   request = context.expand( '${'+"$name"+'#RawRequest}' )
   zRequest = zip(request)
         assertion = assertion.replaceAll( /([^a-zA-Z0-9 _,:])/, '-' )
         message = message.replaceAll( /([^a-zA-Z0-9 _,:\[\]])/, '-' )
         if (assertion.length() > 128) {assertion = assertion.substr(0,128)}
         if (message.length() > 1024) {message = message.substr(0,1024)}
         lquery = "INSERT INTO test.soapuiresults (uuid,assertion,tstatus,message,step,request,response,tsuite,tcase) VALUES ('"+uuid+"','"+assertion+"','F','"+message+"','"+tname+"','"+zRequest+"','"+zResponse+"','"+testsuitename+"','"+testcasename+"');"
         try {
          if (next) {
           next = false
         } catch (Exception e) {
          log.error e.message
         comma = false
         getinfo = false

 if (next) {
  lquery = 'INSERT INTO test.soapuiresults (uuid,assertion,tstatus,message,step,tsuite,tcase) VALUES ("'+uuid+'","No Errors","P","All Passed","All","'+testsuitename+'","'+testcasename+'");'
  try {
  } catch (Exception e) {
          log.error e.message
def zip(String s){
def targetStream = new ByteArrayOutputStream()
def zipStream = new GZIPOutputStream(targetStream)
def zipped = targetStream.toByteArray()
return zipped.encodeBase64()

  • Please note that in order to save HTML/XML requests and responses, I preferred to zip them and put them into database as a blob field. Later, I will use the unzipped text as a text attachment in Allure dashboard
  • If you have the Ready!API Pro version, you can add this code only once to the event handler (right-click on project node, select events, and create a "TestRunListener.afterRun" event)
  • now you need to retrieve data from database (using uuid selector) in your Allure Maven TestNG suite, and simply pass or fail them with regular Assert() method.