SoapUI Groovy Scripting: Dynamic API Tests Guide

SoapUI Groovy Scripting: Dynamic API Tests Guide

Static SOAP tests — send a request, assert a response — cover the basics. But real-world testing requires dynamic behavior: generating unique values, extracting data from one response to use in the next request, conditional logic based on response content, and loops. SoapUI's Groovy scripting engine handles all of this.

Groovy is a JVM language that sits close to Java but with a much lighter syntax. If you have Java experience it will feel familiar immediately; if not, the basics are readable enough to pick up in a few hours. This guide covers the scripting approaches that come up most often in real SOAP testing work.

Where Groovy Scripts Run in SoapUI

SoapUI has several places where you can run Groovy scripts:

  • Script TestStep — a standalone test step that executes arbitrary Groovy code
  • Script Assertion — an assertion type that validates a response using Groovy logic
  • Setup Script — runs before a test case executes
  • TearDown Script — runs after a test case completes
  • Event Listeners — respond to project-level events

The most common uses are Script TestSteps (for setup logic, data manipulation, and logging) and Script Assertions (for complex response validation).

The SoapUI Groovy Context

Every script in SoapUI has access to a context object and a log object. These are your primary tools.

// log — write to the SoapUI log panel
log.info "Test started"
log.error "Something went wrong"
log.debug "Debug value: ${someVariable}"

// context — the current test context
// Access the test runner
def testRunner = context.testRunner

// Access the test case
def testCase = testRunner.testCase

// Access a property from the test case
def baseUrl = testCase.getPropertyValue("BaseUrl")

The log object is invaluable for debugging. Anything written to log.info appears in the Script Log panel at the bottom of the SoapUI window.

Script TestStep: Setup and Teardown

A Script TestStep runs Groovy code as a test step — useful for setting up dynamic values before a SOAP request.

Generating a Unique Request ID

Many enterprise SOAP services require a unique transaction ID in the SOAP header. Generate one dynamically rather than hardcoding it:

import java.util.UUID

// Generate a UUID and store it as a test case property
def transactionId = UUID.randomUUID().toString()
testRunner.testCase.setPropertyValue("TransactionId", transactionId)

log.info "Generated TransactionId: ${transactionId}"

Then reference this property in your SOAP request XML:

<soapenv:Header>
  <txn:TransactionId>${#TestCase#TransactionId}</txn:TransactionId>
</soapenv:Header>

Generating a Timestamp

Timestamp-based headers are common in WS-Security and audit logging:

import java.text.SimpleDateFormat
import java.util.Date

def sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
sdf.setTimeZone(TimeZone.getTimeZone("UTC"))
def timestamp = sdf.format(new Date())

testRunner.testCase.setPropertyValue("RequestTimestamp", timestamp)
log.info "Request timestamp: ${timestamp}"

Property Transfer: Chaining SOAP Requests

One of the most common patterns in SOAP testing is chaining: use a value from response A as an input to request B. SoapUI has a built-in Property Transfer test step, but you can also do it in Groovy for more control.

Example: Extract Session Token and Use in Next Request

// Script TestStep — runs after a Login request step
// Get the response from the Login step
def loginStep = testRunner.testCase.getTestStepByName("Login Request")
def loginResponse = loginStep.getPropertyValue("Response")

// Parse the XML response
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def holder = groovyUtils.getXmlHolder(loginResponse)

// Extract the session token using XPath
def sessionToken = holder.getNodeValue("//auth:SessionToken")

if (!sessionToken) {
    throw new Exception("Login failed — no session token in response")
}

// Store for use in subsequent requests
testRunner.testCase.setPropertyValue("SessionToken", sessionToken)
log.info "Session token extracted: ${sessionToken.substring(0, 8)}..."

The GroovyUtils.getXmlHolder() method is SoapUI's built-in XML parser — it handles namespaces properly, which is critical for SOAP XML.

Script Assertion: Custom Response Validation

Script Assertions let you write validation logic that goes beyond what XPath Match and Contains assertions can express.

A Script Assertion receives two implicit objects:

  • messageExchange — the request/response pair
  • context — the test context
// Script Assertion on a GetBalance response
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def holder = groovyUtils.getXmlHolder(messageExchange.responseContent)

// Extract balance value
def balanceStr = holder.getNodeValue("//acc:Balance")

if (balanceStr == null) {
    throw new Exception("Balance element not found in response")
}

def balance = balanceStr.toBigDecimal()

// Business rule: balance should never be negative for this account type
if (balance < 0) {
    throw new Exception("Balance is negative: ${balance}. " +
        "Business rule violation for savings account type.")
}

log.info "Balance assertion passed: ${balance}"

Throwing an exception inside a Script Assertion marks the assertion as failed with your message as the failure reason. Return normally (or explicitly return) for a pass.

Validating Response Time

Script Assertions can also check non-functional requirements:

// Assert response time is under 2 seconds
def responseTime = messageExchange.timeTaken

if (responseTime > 2000) {
    throw new Exception("Response time ${responseTime}ms exceeds 2000ms SLA")
}

log.info "Response time: ${responseTime}ms — within SLA"

Loops: Data-Driven Testing in Script Steps

When you need to run the same SOAP operation multiple times with different inputs, a Groovy loop in a Script TestStep can drive the test:

// Test multiple account IDs from a list
def accountIds = ["ACC001", "ACC002", "ACC003", "ACC999"]

def getBalanceStep = testRunner.testCase.getTestStepByName("GetBalance Request")

accountIds.each { accountId ->
    log.info "Testing account: ${accountId}"
    
    // Set the account ID property
    testRunner.testCase.setPropertyValue("AccountId", accountId)
    
    // Run the GetBalance step
    def result = getBalanceStep.run(testRunner, context)
    
    if (result.isFailed()) {
        log.error "Failed for account: ${accountId} — ${result.messages}"
    } else {
        log.info "Passed for account: ${accountId}"
    }
}

For larger datasets, read from a file instead of a hardcoded list:

def dataFile = new File("/path/to/test-accounts.csv")

dataFile.eachLine { line ->
    if (line.startsWith("accountId")) return // skip header
    
    def fields = line.split(",")
    def accountId = fields[0].trim()
    def expectedBalance = fields[1].trim().toBigDecimal()
    
    testRunner.testCase.setPropertyValue("AccountId", accountId)
    testRunner.testCase.setPropertyValue("ExpectedBalance", expectedBalance.toString())
    
    def result = testRunner.testCase.getTestStepByName("GetBalance Request")
                                    .run(testRunner, context)
    
    log.info "Account ${accountId}: ${result.isFailed() ? 'FAILED' : 'PASSED'}"
}

Calling External Services from Groovy

Sometimes tests need to interact with databases, REST APIs, or other services to set up test data or validate side effects.

HTTP call from Groovy

import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.Method.POST
import static groovyx.net.http.ContentType.JSON

def http = new HTTPBuilder("http://internal-api.example.com")

http.request(POST, JSON) { req ->
    uri.path = "/test-data/reset"
    body = [environment: "qa", accountId: "ACC001"]
    
    response.success = { resp, json ->
        log.info "Test data reset successful: ${json.message}"
    }
    
    response.failure = { resp ->
        throw new Exception("Test data reset failed: HTTP ${resp.status}")
    }
}

JDBC database query

import groovy.sql.Sql

def sql = Sql.newInstance(
    "jdbc:postgresql://db.example.com:5432/testdb",
    "testuser",
    "testpassword",
    "org.postgresql.Driver"
)

def row = sql.firstRow("SELECT balance FROM accounts WHERE id = ?", ["ACC001"])
def dbBalance = row.balance

testRunner.testCase.setPropertyValue("DBExpectedBalance", dbBalance.toString())
log.info "Database balance for ACC001: ${dbBalance}"

sql.close()

Note: You need the JDBC driver JAR in SoapUI's lib directory for database connections to work.

Error Handling and Test Failure

Groovy's exception handling works as expected:

try {
    def step = testRunner.testCase.getTestStepByName("CreateOrder Request")
    def result = step.run(testRunner, context)
    
    if (result.isFailed()) {
        // Log detailed failure info
        result.messages.each { message ->
            log.error "Failure: ${message}"
        }
        testRunner.fail("CreateOrder step failed — see log for details")
    }
} catch (Exception e) {
    log.error "Unexpected exception: ${e.message}"
    testRunner.fail("Script error: ${e.message}")
}

testRunner.fail(message) marks the test case as failed with your message. Use this instead of throwing an exception from a Script TestStep — exceptions cause a less clean failure message.

Practical Tips

Use project-level properties for configuration. Base URLs, credentials, timeouts — anything that changes between environments — should be project or suite properties, not hardcoded in scripts. This makes your test suite portable.

Keep scripts short. If a Groovy script exceeds 50 lines, consider whether the logic belongs in a Script TestStep (setup/orchestration) or a Script Assertion (validation). Mixing both in one script makes maintenance hard.

The GroovyUtils class is your friend for XML. SoapUI's com.eviware.soapui.support.GroovyUtils handles namespace resolution properly. Avoid rolling your own XML parsing with XmlSlurper unless you have a specific reason — namespace issues with SOAP XML are a common source of bugs.

Log generously during development. Comment out verbose log statements (or change to log.debug) once tests stabilize. Verbose logging slows down large test suite runs.

Going Further

Groovy scripting in SoapUI is essentially unlimited — you have access to the full JVM ecosystem. Teams with complex SOAP testing requirements use Groovy to:

  • Generate cryptographic signatures for WS-Security manually
  • Call test management systems (Jira, TestRail) to log results
  • Integrate with CI systems to retrieve dynamic configuration
  • Implement complex multi-step transactional test scenarios

For teams that also need UI-level coverage alongside their SoapUI work, HelpMeTest handles browser automation in plain English — no Groovy required. The two tools cover different layers cleanly: SoapUI for SOAP protocol testing, HelpMeTest for end-to-end user journey validation.

Groovy scripting is what separates a SoapUI power user from someone who can only run pre-generated requests. Once you are comfortable with property transfer, script assertions, and the XML holder, you can handle virtually any SOAP testing scenario.

Read more

Start now free