2

I have generate a report based on an execution of a test suite where it creates a folder directory and insert a file displaying the report. This is compiled within a TearDown Script at Test Suite level. Below is the code:

def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context) 
def dataFolder = groovyUtils.projectPath
def failedTestCases = 0
def succeedTestCases = 0
def totalTestCases = 0
def testCaseFailed = ""
def testCaseSucceed = ""
def date = new Date()
def folderTime = date.format("yyyy-MM-dd HH-mm-ss")
def hotelId = context.getProperty('hotelid')
def hotelname = context.getProperty('hotelname')
def hoteltype = context.getProperty('hoteltype')
//def propertyValues = ""
//def correlationid = messageExchange.modelItem.testStep.testCase.testSuite.Project.namegetPropertyValue("correlationid")
//Create a folder directory for the responses
RootResultFolder = dataFolder + "\\Test Reports" + "\\xxx_WebAPI - " + folderTime + "\\"
CreateResultFolder = new File(RootResultFolder)
CreateResultFolder.mkdir()

//context.setProperty("RootResultFolder", RootResultFolder)

def fileName = "WebAPI Test Report.txt"
def rootFolder = RootResultFolder + fileName 
def logFile = new File(rootFolder)



if(logFile.exists())
{

 log.info("Error a file named " + fileName + "already exisits")
}
    else
{
runner.results.each { testCaseResult ->
    def name = testCaseResult.testCase.name
    totalTestCases++
    if(testCaseResult.status.toString() == 'FAILED'){
        failedTestCases ++
        testCaseFailed += "- $name - HAS FAILED \n\n"
       //propertyValues += "hotelid - $hotelid, hotelname - $hotelname, hoteltype - $hoteltype \n\n"
        testCaseResult.results.each{ testStepResults ->
            testStepResults.messages.each() { msg -> log.info msg } 
        }
    }else{
        succeedTestCases ++
        testCaseSucceed += "- $name - SUCCEED \n\n"
        testCaseResult.results.each{ testStepResults ->
        testStepResults.messages.each() { msg -> log.info msg } 
        }
    }
}
}


logFile.write   "TOTAL TEST CASES SUCCEED: $succeedTestCases of $totalTestCases" + "\n\n" +
            testCaseSucceed + "---\n\n" +
            "TOTAL TEST CASES FAILED: $failedTestCases of $totalTestCases" + "\n\n" +
            testCaseFailed + "\n\n"

What I actually want to do is move the code from Test Suite level and place it in the tear down script at Project level. Now when I run the code from there, it does not generate the file, I'm assuming I need to place the correct paths in as I am not moving to test suite to test case but from project to test suite to testcase to test steps.

My question is really on syntax, I want to develop a report when the whole project is run, it outputs the following results:

  • Project Name - is it success or failed. If one suite failed then project fails else it passes
  • Test Suite - Take name of each test suite in project and if passes then place 'Succeed' next to name of test suite else place 'Failed' next to name of test suite
  • Name of all test cases within test suite. Like the one in screenshot really, 'succeed' next to test cases that have passed and 'failed' next to those that haven't.
  • Finally the property values. If a test case has failed, capture the property values for that failed test case so we can track which values were entered that caused the failure of the test.

Can somebody help me with the relevant syntax to perform these so then I can peice it into my code and manipulate?

UPDATE:

def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context) 
def dataFolder = groovyUtils.projectPath
def date = new Date()
def folderTime = date.format("yyyy-MM-dd HH-mm-ss")

//Create a folder directory for the responses
RootResultFolder = dataFolder + "\\Test Reports" + "\\xxx - " + folderTime + "\\"
CreateResultFolder = new File(RootResultFolder)
CreateResultFolder.mkdir()*/

//context.setProperty("RootResultFolder", RootResultFolder)

def reportFileName = "WebAPI Test Report.txt"
def rootFolder = RootResultFolder + reportFileName 
def logFile = new File(rootFolder)
BruceyBandit
  • 3,978
  • 19
  • 72
  • 144
  • Is this question different form your previous question here - http://stackoverflow.com/questions/41662721/report-not-generated-from-tear-down-script-when-a-test-case-fails? or just a duplicate question? – Rao Jan 18 '17 at 04:30
  • Hi Rao, this question is different. The last question was asking about a test report that was not generating from test suite level when a test suite fails. This one is asking how to create a test report from project level with the relevant information required that's mentioned in the above question – BruceyBandit Jan 18 '17 at 07:19
  • Does not differ much in my view. Have you tried using `testrunner` to executes tests as it generate the report in `junit` style instead of you writing it? Later a nice html reports can be generate out of junit style report which is more standard and superior than yours at first glance. – Rao Jan 18 '17 at 07:54
  • We just want one single report to be outputted after the whole project has run so I think doing it via the test runner and junit style may be an issue because I am assuming it will create a report for every single test? – BruceyBandit Jan 18 '17 at 08:24
  • I think best way for me to ask the question is that what is the syntax for trying to grab test suite result, test case result, project result and test step result? I have test case and step results but if I can get test suite and project result then I think I can develop it from there – BruceyBandit Jan 18 '17 at 08:25
  • [Here](https://www.soapui.org/reporting/generating-html-reports.html) you can find how sample report looks like what I was talking about. Anyways, what is issue that you face? or any stacktrace ? – Rao Jan 18 '17 at 08:27
  • The problem I'm facing is just the synatx really. I want to know the syntax on knowing how to drill down each leavel from project... test suite... etc so then I can add their results in the report. The junit link you sent me is very good but our company are not going to pay for the pro version, that's why I am writing a groovy script to create our own report on a free version. Problem is that there is not many articles on groovy scripting a report, especially from project level – BruceyBandit Jan 18 '17 at 08:41
  • It is possible to create junit report without pro edition as well. – Rao Jan 18 '17 at 08:56
  • Oh is it? Through coding or just following the tutorial you sent me? I will see if I can find the reporting option – BruceyBandit Jan 18 '17 at 08:59
  • Please see here https://softwaretestersforum.blogspot.com/2013/03/generate-junit-style-html-reports-in.html or http://stackoverflow.com/questions/12855740/coversion-of-junit-xml-report-into-html-form or https://community.smartbear.com/t5/SoapUI-Open-Source/Junit-Style-Report-using-SoapUI-Open-Source/td-p/113298 – Rao Jan 18 '17 at 09:16
  • @Rao, i think it will be easier for me to code it. Especially when testers are running it within TFS and not from local machine – BruceyBandit Jan 18 '17 at 15:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/133470/discussion-between-rao-and-bruceybandit). – Rao Jan 18 '17 at 15:31
  • BruceyBandit, would you please clarify on 4th point `capture the property values for that failed test`? Do you have any custom properties set test case level? Any way they are going to be there, nothing gets modified right? Please clarify. – Rao Jan 20 '17 at 02:06
  • Yes that's right those properties are set test case level as custom properties and they are hotelid, hotelname and hoteltype as displayed in code. When a test case fails, I want the values within those properties to be added in the report. And these values do not get modified during a test run per request – BruceyBandit Jan 20 '17 at 04:17

1 Answers1

11

If you look at the TearDown Script of the project, it shows as below i.e., the variables already initialized by soapui.

enter image description here

Issue with your script
So if you look at it, there is runner variable. Also the same variable is available at TearDown script of test suite level. But, these are instances of different Objects. The script used in OP was of suite level which you aware and that is why you are not seeing in the result.

Here is the project level TearDown Script and following in line comments.

/**
*
* Below is the TearDown script for SoapUI Project level
* Which create a custom report in a  given file
* Modify the variable "reportFileName" below
*
**/
//Modify the file as needed for report file
//def reportFileName = '/tmp/abctestreport.txt'
//Adding the below as user wants specific directory
//Get the project path

def dataFolder =  new com.eviware.soapui.support.GroovyUtils(context).projectPath

//Create today's date for storing response
def today = new Date().format("yyyy-MM-dd")

def filePrefix = "${dataFolder}/TestReports/xxx_WebAPI_${today}" as String

def fileNamePart = new Date().format("yyyy-MM-dd'T'HH.mm.ss")

//creating filename dynamically.
def reportFileName = "${filePrefix}/xxx_WebAPI_TestReport_${fileNamePart}.txt" as String


//NOTE: Not required to edit beyond this point

/**
* This class holds the test case details
**/
class TestCaseResultHolder {
    def log
    Map<String, String> properties = [:]
    boolean status

    def createProperties(testCase) {
        testCase.getPropertyNames().each { key ->
            properties[key] = testCase.getPropertyValue(key)
        }       
    }

    def getCaseResult(caseRunner, caseName) {
        log.info "Checking test case status ${caseName}"
        if ( caseRunner.status.toString() == 'FAILED' ){
            log.error "Test case $caseName has failed"
            for ( stepResult in caseRunner?.results ){
                stepResult.messages.each() { msg -> log.info msg }
            }
            return false
        } else {
            log.info "${caseName} is passed"
        }
        true
    }

    def buildCaseResult(caseRunner, caseName) {
        status = getCaseResult(caseRunner, caseName)
        if (!status) {
            createProperties(caseRunner.testCase)
        }
    }

}

/**
* This class holds the test suite details
**/
class SuiteResultsHolder {

    def log
    Map<String, TestCaseResultHolder> casaeResults = [:]
    int testCaseCount = 0
    int passedCasesCount = 0
    int failedCasesCount = 0

    def buildSuiteResults(suiteRunner, suiteName){      
        log.info "Building results of test suite ${suiteName}"
        for ( caseRunner in suiteRunner?.results ) {
            def caseName = caseRunner.testCase.name
            testCaseCount++
            def tcHolder = new TestCaseResultHolder(log: log)
            tcHolder.buildCaseResult(caseRunner, caseName)          
            casaeResults[caseName] = tcHolder
            if (tcHolder.status) {
                passedCasesCount++
            } else {
                failedCasesCount++
            }
        }
    }

    def getStatus() {
        (0 < failedCasesCount) ? false : true
    }

}

/**
* This class holds the project details
**/
class ProjectResultsHolder {

    def log
    Map<String, SuiteResultsHolder> suiteResults = [:]
    int suiteCount = 0
    int passedSuitecount = 0
    int failedSuiteCount = 0

    def buildProjectResults(projectRunner, projectName) {
        log.info "Building results of test project ${projectName}"          
        for(suiteRunner in projectRunner?.results) {
            def suiteName =  suiteRunner.testSuite.name
            suiteCount++
            def suiteResultsHolder = new SuiteResultsHolder(log: log)
            suiteResultsHolder.buildSuiteResults(suiteRunner, suiteName)
            suiteResults[suiteName] = suiteResultsHolder
            if (suiteResultsHolder.status) {
                passedSuitecount++
            } else {
                failedSuiteCount++
            }
        }
    }

    def getStatus() {
        (0 < failedSuiteCount) ? false : true
    }

}

//Get the status string based on boolean
def getResult(status){ status == true ? 'SUCCEED' : 'FAILED'}

//Draws a line
def drawLine(def letter = '=', def count = 70) { letter.multiply(count)}

//Gets the summary report
def getSummaryReport(project, projectResultHolder) {
    def report = new StringBuffer()
    report.append(drawLine()).append('\n')
    report.append("\t\t\tTest Execution Summary\n")
    report.append(drawLine('-', 60)).append('\n')
    report.append("Project : ${project.name}\n")
    report.append("Result : ${getResult(projectResultHolder.status)}\n")
    report.append("Total test suites executed: ${projectResultHolder.suiteCount}\n")
    report.append("Test suites passed: ${projectResultHolder.passedSuitecount}\n")
    report.append("Test suites failed: ${projectResultHolder.failedSuiteCount}\n")
    report.append(drawLine()).append('\n')
    report
}

//Gets the test case report
def getTestCaseReport(testCaseReport) {
    def report = new StringBuffer()
    report.append(drawLine('-', 60)).append('\n')
    report.append("\t\tTest Case Details:\n")
    report.append(drawLine('-', 60)).append('\n')
    testCaseReport.each { kase, tcReport ->
        report.append("Name : ${kase}\n")
        report.append("Status : ${getResult(tcReport.status)}\n")
        if (!tcReport.status) {
            report.append("Properties : ${tcReport.properties.toString()}\n")
        }
    }
    report
}

//Get the detailed report
def getDetailedReport(projectResultHolder) {
    def report = new StringBuffer()
    report.append(drawLine()).append('\n')
    report.append("\t\t\tTest Execution Detailed Report\n")
    report.append(drawLine()).append('\n')
    projectResultHolder.suiteResults.each { suite, details ->
        report.append("Test Suite : ${suite}\n")
        report.append("Result : ${getResult(details.status)}\n")
        report.append("Total Cases : ${details.testCaseCount}\n")
        report.append("Cases Passed : ${details.passedCasesCount}\n")
        report.append("Cases Failed: ${details.failedCasesCount}\n")
        report.append(getTestCaseReport(details.casaeResults))
        report.append(drawLine()).append('\n')
        report.append(drawLine()).append('\n')
    }
    report
}

//Save the contents to a file
def saveToFile(file, content) {
    if (!file.parentFile.exists()) {
        file.parentFile.mkdirs()
        log.info "Directory did not exist, created"
    }
    file.write(content) 
    assert file.exists(), "${file.name} not created"
}

def holder = new ProjectResultsHolder(log: log)
holder.buildProjectResults(runner, project.name)

def finalReport = new StringBuffer()
finalReport.append(getSummaryReport(project, holder))
finalReport.append(getDetailedReport(holder))

def reportFile = new File(reportFileName)
saveToFile(reportFile, finalReport.toString())

And here is the generated output:
enter image description here

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
Rao
  • 20,781
  • 11
  • 57
  • 77
  • That looks really good. Only one little question, I want the file to be put in a folder directory. In my code I always create a new folder directory when this script is run and the file will go into that created folder directory. However when i implement this code, the folder is created but there's no file within. Do you know why this is and what I need to change? If you look at the UPDATE of my question, i simply added the snippet of code under the UPDATE above the code you have given me but placed the reportFileName within – BruceyBandit Jan 20 '17 at 11:40
  • All you need to do is provide the absolute file path for `reportFileName` variable. You do not have to create it manually, it automatically creates. Even if directory exists, it will write. And appreciate if you can accept the answer and upvote when it is helpful. If you do not want to hard code the path, use it as project property. – Rao Jan 20 '17 at 11:43
  • I request you to give a try running a simple project quickly to see it in `Action`. What I did was created multiple suites with a test case and a test step with a groovy script step which some times fails and some times passes dynamically. If you can test it with your real project, that would be even good. So, your question will be automatically cleared. – Rao Jan 20 '17 at 11:51
  • will accept your answer. I ran it with my project and it works really well. Like I said only little issue is that the file itslef is replaced but would like new files to be generated within their own folders. So example, run project, a folder is created with the file inside, run project again and a new folder is created with its file contained inside. That's all i need and it's perfect – BruceyBandit Jan 20 '17 at 11:55
  • Ok, you can use the code, from my [previous answer to your question](http://stackoverflow.com/questions/41662120/files-not-being-stored-in-folder-directory/41681823#41681823), to create the sub directories automatically, to be specific the lines after `saveToFile` method from previous answer create sub dir automatically. Give it a try. Otherwise, I can update here in this answer again. Assuming that you have tried now at least with fixed `reportFileName` to get the report in above mentioned output fashion. – Rao Jan 20 '17 at 11:59
  • I've always wondered in your previous answer where you retrieve f from saveToFile(f, context.response)? – BruceyBandit Jan 20 '17 at 12:08
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/133637/discussion-between-rao-and-bruceybandit). – Rao Jan 20 '17 at 12:09
  • Hmmm, same issue, generate folder but no file. I think it would be better to see the way you implement it and compare to see the differences. Btw the bounty will be awarded when it lets me in 3 hours – BruceyBandit Jan 20 '17 at 12:18
  • Pefect, thank you very much for all your help RAO. I'll award the bounty once it enables me to do so – BruceyBandit Jan 20 '17 at 14:29
  • Glad that it helped and patiently trying it out. – Rao Jan 20 '17 at 14:30
  • @Rao, Very useful, +1 for your efforts and sharing knowledge. – Raju Sep 17 '17 at 09:11
  • @Raju, glad that you find it useful. Thank you. – Rao Sep 17 '17 at 10:18