SoapUI is a convenient GUI application for testing REST API and SOAP Web Services, which exists in 2 versions: Pro and Open-source. Here we will consider the Open-source version and how to use it with Groovy scripts.
The SoapUI application is written in Groovy. We will add scripts in our tests written in Groovy (although Java syntax is valid for Groovy as well).
SoapUI is a test case constructor that allows you to build tests from current API queries. Below is a screenshot of SoapUI v. 5.5.0 for Windows:
As a test project, we will use the simple Students application (Node.JS + JS) that creates, edits, and deletes users.
Installation:
>git clone https://github.com/EdIzotov/studentsApp.git
>cd studentsApp
>npm install
>npm start
WebUI is available in the application http://localhost:3000/, so it will not be difficult for us to see the API requests and transfer them to the SoapUI app:
POST /api/users – create user
GET /api/users – get users
PUT /api/users/ID – edit user
DELETE /api/users/ID – delete user
Creating SoapUI project
Let's create a new REST project:
and then copy requests into the project:
Creating test case
Now let's move on to the test case using the Navigator panel:
If we right-click on "Test Steps" in the Navigator panel, we will see a list of the main elements for building the test:
Now let's come up with some kind of test. The scenario will be as follows:
- Get a list of users.
- Create a new user. Check that it is created.
- Edit this user and check the changes.
- Delete the user and verify that it is deleted.
As action requests, it will look like this:
- GET /api/users - fix the users that exist in the system
- POST /api/users - create a new user
- GET /api/users - get a new list of users to check
- PUT /api/users/ID - edit the user
- GET /api/users - get a new list of users to check
- DELETE /api/users/ID - delete the user
- GET /api/users - get a new list to check
Add a REST request for each step:
Pay attention:
To generate test data, you can invoke static Java methods from anywhere using the "${}" syntax. Also note that many tasks of transferring properties from one step to another one can be solved using the "Property Transfer" element. But we will use Groovy Scripts, because Groovy provides more flexible features. In addition, not all tasks can be solved using Property Transfer, unlike scripts.
Groovy scripting
Values initialization
Now you will learn how to set a property value in SoapUI using Groovy Script. At first, create the first Groovy Script which will set the initial values like this below:
Now a little attention to the scripts. There are 2 types of scripts in SoapUI: scripts - test steps, scripts - assertions. Script Assertions will be discussed later in this article. The difference between the two is that Groovy Script is called with the "log", "context", "testRunner" variables, and the Script Assertion is called with the "log", "context", "messageExchange" variables.
We'll immediately deal with the "log" variable in this script and run it by clicking on the launch button on the panel:
log.class
You will receive a message that will show the class of this variable. This will prove to be a classic Logger from Apache.
Please note that unlike Java, where it is customary to use getters and setters, they are created automatically in Groovy. And we can handle both in the Java style - log.getClass () - and in the Groovy style - log.class. They are the same.
If you want to see all the methods of the Logger class, we can do this through reflection (because the script editor in SoapUI does not offer auto-substitution as well-known IDEs):
log.class.methods.collect {method -> return method}
Basically, we will need standard logger methods: log.info, log.debug, log.warn, log.error
For Groovy methods, it is not necessary to use parentheses to call a method if you do not pass any arguments. The "collect" method iterates over the collection, and inside the body of the method, you can change the value of the element. The body uses an anonymous block of code called "Closure".
Groovy also has a unary "Closure" that you can use if you don't need to manipulate the elements of the collection - this is a reserved word "it":
log.class.methods.collect {it}
Now let's deal with the "context" variable:
Context is used to store intermediate results in the current test. We will use it to transfer values between test steps:
context.userName = UUID.randomUUID().toString()
context.userPhone = System.currentTimeMillis()
We use the generation of random values that practically ensure uniqueness (UUID and Unix Timestamp). Also note that when calling these methods, we must use parentheses, because these are Java methods.
We will also transfer this data to CreateNewUser. To do this, we need to add 2 fields of the style "Plain" to a CreateUser request:
And now we will pass values to these fields in Groovy Script. For this, we will use the "testRunner" variable. "testRunner" is used to control test steps and store/pass values to them. In the above way (through reflection), you can easily get a list of methods for working with this variable:
testRunner.testCase.getTestStepByName('CreateNewUser').setPropertyValue('userName', context.userName)
testRunner.testCase.getTestStepByName('CreateNewUser').setPropertyValue('userPhone', context.userPhone)
Basically, our first step is done:
GET /users initial values
Now consider the GetUsersInitial step. We will get the current list of users in the step. It is recommended to add checks for each step using Assertions at the bottom of the window:
The list of possible assertions includes those that can be used without Groovy. But we will add all checks through Script Assertion:
First, consider the messageExchange variable:
messageExchange.class.methods.collect {it}
And we will see that there are some methods for working with the Request => Response pair. For example, getResponseContent() and getResponseStatusCode(). Let's try to call and get data:
messageExchange.responseStatusCode
And then we want to check:
assert messageExchange.responseStatusCode == 200
Now get the response body and save it in the context:
context.getUsersInitial = messageExchange.responseContent
The step is done:
Creating user with POST /api/users
Handle the CreateNewUser request. To use our "username" and "userPhone" values in the request body, we need the ${} construct:
And similar to the previous REST request, add Assertion:
assert messageExchange.responseStatusCode == 200
Passing user ID to next steps
Now add one more Groovy Script to ensure that the created user ID is passed to edit and delete requests:
This way we get the response body of the previous request:
testRunner.testCase.getTestStepByName('CreateNewUser').properties.Response.value
But note that we get a String, but we need to get a Map object (JSON, if you want) to get a specific ID. To do this, use the built-in groovy.json library:
import groovy.json.JsonSlurper
id = new JsonSlurper().parseText(testRunner.testCase.getTestStepByName('CreateNewUser').properties.Response.value).id.toString()
And we pass in the "DELETE" and "PUT" requests in the ID field of type Template, which we created in advance (seen in the screenshots above):
testRunner.testCase.getTestStepByName('EditUser').setPropertyValue('ID', id)
testRunner.testCase.getTestStepByName('DeleteUser').setPropertyValue('ID', id)
And save into the context:
context.id = id
Checking that user was created
Next, we process the request in which we verify that the user is created. In the GetUsersAfterCreate request, we can immediately proceed to the Script Assertion. Of course, we need the JsonSlurper library to convert String to List of Maps:
import groovy.json.JsonSlurper
Check the status code:
assert messageExchange.responseStatusCode == 200
Parse JSON and check that there are more users:
json = new JsonSlurper()
users = json.parseText(context.getUsersInitial)
newUsers = json.parseText(messageExchange.responseContent)
assert newUsers.size() > users.size(),
Now we'll check that our user is in the new list of users:
userExists = false
newUsers.each {
if (it.id.toString() == context.id) {
if (it.name == context.userName && it.phone == context.userPhone) {
userExists = true
}
}
}
assert userExists
This Script Assertion is done:
Generating new data
Next, we create a new Groovy Script, generate new data for editing and transfer it:
context.userName = UUID.randomUUID().toString()
context.userPhone = System.currentTimeMillis().toString()
testRunner.testCase.getTestStepByName('EditUser').setPropertyValue('userName', context.userName)
testRunner.testCase.getTestStepByName('EditUser').setPropertyValue('userPhone', context.userPhone)
Edit user with PUT /api/users/ID
Let's edit the EditUser step:
And add into this step Script Assertion:
assert messageExchange.responseStatusCode == 204
Checking that the user was edited
Add Script Assertion in GetUsersAfterEdit request:
import groovy.json.JsonSlurper
assert messageExchange.responseStatusCode == 200
json = new JsonSlurper()
users = json.parseText(messageExchange.responseContent)
userExists = false
users.each {
if (it.id.toString() == context.id) {
if (it.name == context.userName && it.phone == context.userPhone) {
userExists = true
}
}
}
assert userExists
Removing the user
Add Script Assertion in DeleteUser request:
assert messageExchange.responseStatusCode == 204
Checking that the user was removed
Finally, add Script Assertion to the last request to verify that the user has been deleted:
import groovy.json.JsonSlurper
assert messageExchange.responseStatusCode == 200
json = new JsonSlurper()
users = json.parseText(messageExchange.responseContent)
userExists = true
users.each {
if (it.id.toString() == context.id) {
userExists = false
}
}
assert userExists
Results
Now we can complete the entire test:
Conclusion
SoapUI is a powerful GUI tool where you can create API tests. If the functionality of the open-source version is not enough, then we can always use Groovy scripts. The main thing in the scripts used is the "context" variable through which we can exchange data between test steps, Groovy Scripts and Script Assertions. Also in Groovy Script we use the "testRunner" variable to access other test objects, and in Script Assertion we use "messageExchane" to access the contents of the current test step.