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 paircontext— 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.