soapUI - Creating Dynamic MockServices
Although the static MockOperation and MockResponse model can give you some flexibility, it is often useful to add more dynamic behavior to your services allowing you to mock more complex service functionality. For example you might want to:
- Transfer values from the request to the response, perhaps modifying them on the way
- Read some data in a request and use its value to select which response to return
- Read the response (or parts of it) from a database instead of having it statically in soapUI
- Manually create a custom HTTP response
- Etc...
Let’s have a look at how to achieve these in particular, but we’ll first give you a general overview of how you can make your MockServices more dynamic.
1. MockService Scripting Overview
A quick recap of the MockService model in soapUI: A MockService contains an arbitrary number of MockOperations, which each mock some WSDL operation in the containing project. These MockOperations can mock operations in different WSDL services, so a single MockService can mock any number of WSDLs to any extent. Each MockOperation contains an arbitrary number of MockResponse messages, i.e. messages that are returned to the client when the MockOperation is invoked. Exactly which message to return is decided by the MockOperation dispatch method, for which several are available; sequence, random, script, etc.
For the MockService itself there are number of scripting events available:
- Start and Stop scripts; these are useful for opening or closing shared objects (for example collections or database connections) which can be made accessible to scripts “further down” in the mock object hierarchy.
- OnRequest script; this is the main handler for simulating non-SOAP behavior (REST, etc); it is called before soapUI does any internal dispatching and has the possibility to return any kind of response to the client (thus bypassing the whole soapUI dispatch mechanism)
- AfterRequest script: primarily intended for custom logging and reporting functionality
All these are available from the MockService window:
For MockOperations one scripting possibility is available; selecting the “Script” option for a MockOperation Dispatch allows you to use script that decides which MockResponse to return to the client, an example of this will be further down
Finally, for a MockResponse you can specify a “Response Script” :
This allows for response-specific script functionality that will be executed before the containing MockResponse message is created and returned to the client; this is the primary place to generate dynamic response content.
In soapUI Pro you can also define project-level event handlers to specify functionality the will be run for all MockServices in your project;
The available Mock-Related handlers are:
- MockRunListener.onMockResult – corresponds to the MockService AfterRequest script
- MockRunListener.onMockRunerStart – corresponds to the MockService Start script
- MockRunListener.onMockRunerStop – corresponds to the MockService Stop script
- MockRunListener.onMockRequest – corresponds to the MockService OnRequest Script
2. Mock Handler Objects
A number of objects are commonly available in most scripts; here comes a quick overview with links to their corresponding javadoc:
- context – used for storing MockService-wide objects, for example database connections, security tokens, etc
- requestContext – used for storing Mock Request-wide objects, for example dynamic content to be inserted into the response message
- mockRequest – an object corresponding to the actual request made to the MockService. Through this object you can get hold of the incoming message, its headers, etc. Also the underlying HttpRequest and HttpResponse objects are available for “low-level” processing
- mockResult – an object encapsulating the result of the dispatched request, this is available in the MockService.afterRequest script.
- mockResponse/mockOperation – objects available at the corresponding levels for accessing the underlying configuration and functionality
- mockRunner – the object corresponding to the execution of the MockService; this gives you access to previous mock results, etc
Ok, now let’s dive in to see how these can be used!
3. Transferring values from the request to the response
This is straightforward scripting done at the MockResponse level; the script uses properties of the mockRequest object to get hold of the incoming request, extracts the desired values via XPath and then writes the created result to a requestContext property:
1.
// create XmlHolder for request content
2.
def
holder =
new
com.eviware.soapui.support.XmlHolder( mockRequest.requestContent )
3.
4.
// get arguments and sum
5.
def
arg1 = Integer.parseInt( holder[
"//arg1"
] )
6.
def
arg2 = Integer.parseInt( holder[
"//arg2"
] )
7.
8.
requestContext.sum = arg1 + arg2
As you can see the extracted value is assigned to a “sum” property in the requestContext (which is specific for this request). This is then used in the response with standard property-expansion:
The panel to the left shows the last request sent to our MockOperation.
4. Creating the response from the result of a TestCase
This is a bit more elaborate; we’ll create a MockResponse script that first executes a soapUI TestCase and uses its outcome to populate the MockResponse message:
01.
// create XmlHolder for request content
02.
def
holder =
new
com.eviware.soapui.support.XmlHolder( mockRequest.requestContent )
03.
04.
// get target testcase
05.
def
project = mockResponse.mockOperation.mockService.project
06.
def
testCase = project.testSuites[
"TestSuite 1"
].testCases[
"TestCase 1"
]
07.
08.
// set arguments as properties
09.
testCase.setPropertyValue(
"arg1"
, holder[
"//arg1"
] )
10.
testCase.setPropertyValue(
"arg2"
, holder[
"//arg2"
] )
11.
12.
// run testCase
13.
def
runner = testCase.run(
new
com.eviware.soapui.support.types.StringToObjectMap(), false )
14.
if
( runner.status.toString() ==
"FINISHED"
)
15.
requestContext.sum = testCase.getPropertyValue(
"sum"
)
16.
else
17.
requestContext.sum =
"Error: "
+ runner.reason
The script first gets hold of the target TestCase to run, sets some properties with the argument values and then executes it. If all goes well the result is read from another TestCase property and returned via the same mechanism as above, otherwise an error is shown instead.
If we just would have wanted to execute a single request and return that requests response (turning soapUI into a “proxy”), we could do this as follows;
01.
// get target request
02.
def
project = mockResponse.mockOperation.mockService.project
03.
def
request = project.interfaces[
"NewWebServicePortBinding"
].operations[
"sum"
].getRequestByName(
"Request 2"
)
04.
05.
// set request from incoming
06.
request.requestContent = mockRequest.requestContent
07.
08.
// submit request asynchronously
09.
request.submit(
new
com.eviware.soapui.impl.wsdl.WsdlSubmitContext( request ), false )
10.
11.
// save response to context
12.
requestContext.responseMessage = request.responseContentAsXml
Here we assign the entire response to a requestContext property; the actual MockResponse message is just a property-expansion of this property;
5. Selecting a response based on the request
This script is specified at the MockOperation level and uses the same code as above to extract the input values from the incoming request. Based on some validations it returns the name of the MockResponse to return to the client. The script is as follows:
01.
// create XmlHolder for request content
02.
def
holder =
new
com.eviware.soapui.support.XmlHolder( mockRequest.requestContent )
03.
04.
// get arguments
05.
def
arg1 = holder[
"//arg1"
]
06.
def
arg2 = holder[
"//arg2"
]
07.
08.
if
( !com.eviware.soapui.support.StringUtils.hasContent( arg1 ) ||
09.
!com.eviware.soapui.support.StringUtils.hasContent( arg2 ))
10.
return
"Invalid Input Response"
11.
12.
try
13.
{
14.
Integer.parseInt( arg1 )
15.
Integer.parseInt( arg2 )
16.
}
17.
catch
( e )
18.
{
19.
return
"Invalid Input Response"
20.
}
21.
22.
// Select valid response randomly
23.
def
r = Math.random()
24.
if
( r <
0.33
)
25.
return
"Simple Response"
26.
else
if
( r <
0.66
)
27.
return
"Call TestCase"
28.
else
29.
return
"Call Request"
The containing MockOperation contains the used MockResponses:
The requestContext variable available in the script is of course the same as we saw in the previous example, allowing you to pass values from the dispatch script to the MockResponse. For example we can add a “freeMemory” property to the requestContext which we add to all MockResponses for diagnostic purposes:
1.
// add diagnostic
2.
requestContext.freeMemory = Runtime.runtime.freeMemory()
3.
...
This would make it available for property-expansion to all MockResponses defined for the MockOperation;
Which will return
to the client.
6. Read the response from a database
This one requires a bit more work as we need to set up and close a database connection which we can use in our scripts. This is best done in the MockService start script as follows;
01.
import
groovy.sql.Sql
02.
03.
// open connection
04.
def
mockService = mockRunner.mockService
05.
06.
def
sql = Sql.newInstance(
"jdbc:mysql://"
+ mockService.getPropertyValue(
"dbHost"
) +
07.
mockService.getPropertyValue(
"dbName"
),
08.
mockService.getPropertyValue(
"dbUsername"
),
09.
mockService.getPropertyValue(
"dbPassword"
),
"com.mysql.jdbc.Driver"
)
10.
11.
log.info
"Succesfully connected to database"
12.
13.
// save to context
14.
context.dbConnection = sql
Here we set up a connection (using groovys’ built in database support) to the configured database (configuration parameters are taken from the MockService properties) which is then saved in the context, making it available to all scripts further down. A corresponding script to close the connection when the MockService stops, is of course required;
1.
// check for connection in context
2.
if
( context.dbConnection !=
null
)
3.
{
4.
log.info
"Closing database connection"
5.
context.dbConnection.close()
6.
}
So if we just start and stop the MockService we will see the following in the log:
Perfect! In this specific and simplistic example I have a table containing the whole SOAP response to be returned to the client; a value in the request is used to look up which response to return. I’ve chosen to do all this logic in a single MockResponse script:
01.
// create XmlHolder for request content
02.
def
holder =
new
com.eviware.soapui.support.XmlHolder( mockRequest.requestContent )
03.
04.
// get arguments and sum
05.
def
arg1 = Integer.parseInt( holder[
"//arg1"
] )
06.
def
arg2 = Integer.parseInt( holder[
"//arg2"
] )
07.
08.
// get connection and perform query
09.
def
sql = context.dbConnection
10.
def
row = sql.firstRow(
"select * from tb_saved_messages where arg1 = ? and arg2 = ?"
, [arg1, arg2])
11.
12.
// save result to property for response
13.
requestContext.responseMessage = row.responseMessage
Pretty straight forward:
- Extract the required values from the request
- Get the db connection from the context
- Perform your query
- Write the response to a requestContext property
The response message itself only has the property-expansion as content:
This will result in the entire message being written into the response.
7. Creating a custom response
This is currently the only way to mock a REST or more complex HTTP service with soapUI; the OnRequest script on the MockService level gives you direct access to the underlying HttpRequest and HttpResponse objects allowing you to create whatever response you would like to. All you need to be sure of is that the script returns a MockResult object which tells soapUI to stop processing the request. So for example if we want to handle a PUT request we could do the following:
01.
// check for PUT
02.
if
( mockRequest.httpRequest.method ==
"PUT"
)
03.
{
04.
def
result =
new
com.eviware.soapui.impl.wsdl.mock.WsdlMockResult( mockRequest )
05.
06.
// build path
07.
def
path = mockRunner.mockService.docroot + File.separatorChar + mockRequest.httpRequest.queryString
08.
path = path.replace( (
char
)
'/'
, File.separatorChar )
09.
10.
// create File object and check if it doesnt already exists
11.
def
file =
new
File( path )
12.
if
( !file.exists() )
13.
{
14.
// create directories
15.
if
( path.lastIndexOf(
""
+File.separatorChar ) >
1
)
16.
new
File( path.substring(
0
, path.lastIndexOf(
""
+File.separatorChar ))).mkdirs()
17.
18.
// write content
19.
file << mockRequest.httpRequest.inputStream
20.
mockRequest.httpResponse.status =
201
21.
22.
log.info
"File written to [$file.absolutePath]"
23.
}
24.
else
25.
{
26.
mockRequest.httpResponse.status =
403
27.
}
28.
29.
return
result
30.
}
As you can see the script writes the request body to the path specified by the URL using the docroot set in the MockService options dialog as root;
Necessary directories are created and the appropriate status code is returned. Calling for example http://localhost:8299/?/some/path/file.dat with HTTP PUT and a message body will result in the file being created. You can use an HTTP TestRequest Step in soapUI to do this:
And now since the file has been created in your MockServices’ docroot you can just use your web browser and specify http://localhost:8299/some/path/file.dat which will retrieve the corresponding file.
8. Final Words
That's it! The scripting possibilities for MockServices allow you to easily create dynamic and “life-like” MockServices (we even have users that actually implement their services in soapUI and deploy them with the deploy-as-war functionality), this document should have given you a better understanding of the possibilities. Good Luck!
15 comments:
It is perfect time to make some plans for the future and it's time to be happy. I have read this post and if I could I want to suggest you few interesting things or suggestions. Perhaps you could write next articles referring to this article. I want to read even more things about it!
Feel free to surf to my site - White Kidney Beans
Hey exceptional website! Does running a blog like this take
a massive amount work? I've very little understanding of programming but I was hoping to start my own blog soon. Anyway, if you have any ideas or tips for new blog owners please share. I know this is off topic however I simply had to ask. Many thanks!
My blog post Sapphire Electronic Cigarette
Pretty nice post. I just stumbled upon your blog and wanted to say that I've truly enjoyed browsing your blog posts. After all I will be subscribing to your rss feed and I hope you write again soon!
Visit my web blog Male enhancement
Do you have a spam problem on this site; I also am a blogger, and I was curious about your situation;
many of us have developed some nice practices and we are looking
to exchange methods with others, please shoot me an e-mail if interested.
Look into my blog: Buy lift serum
I just like the valuable information you supply in your articles.
I will bookmark your blog and check once more here regularly.
I'm fairly certain I'll learn many new stuff proper right here!
Best of luck for the following!
Look at my web site: Xtrasize Male Enhancement
Someone essentially help to make seriously posts I would state.
That is the first time I frequented your web page and thus far?
I amazed with the analysis you made to create this actual submit extraordinary.
Wonderful task!
Also visit my blog post :: Beta Force Muscle Solution
Wow that was odd. I just wrote an incredibly long comment but after I clicked submit my comment didn't show up. Grrrr... well I'm not writing all that over again.
Anyhow, just wanted to say superb blog!
my web site ... Garcinia Cambogia Extract
I simply could not depart your website prior to suggesting that I really enjoyed the standard information an individual provide in your visitors?
Is going to be back continuously in order to investigate cross-check new posts
Look at my site - Muscle Maximizer Reviews
Thanks for one's marvelous posting! I definitely enjoyed reading it, you
will be a great author. I will be sure to bookmark your blog and will come back later on. I want to encourage you to continue your great job, have a nice evening!
Look at my weblog ijser.info
Just copy/paste from SoapUI's web site. Show your work please.
Nice post!
Wonder, if there easy way to read into Response template with placeholders, fill placeholders variables with autogenerated values and parse it before assigning to Response context?
ecigarette, smokeless cigarettes, smokeless cigarettes, e cigarette, e cigarette, electronic cigarette brands
www0626
adidas soccer shoes
ugg outlet
michael kors outlet
canada goose outlet
ray ban sunglasses
ray ban sunglasses
rockets jerseys
canada goose outlet
true religion jeans
warriors jerseys
kate spade handbags
michael kors outlet online
nike air max 270
nike cortez men
air max 2019
christian louboutin outlet
adidas zx flux
ralph lauren uk
nfl jerseys
cheap nfl jerseys
hermes outlet online
golden goose outlet
off white
jordan 12
supreme
off white outlet
bape t shirt
supreme outlet
yeezy 700
kyrie shoes
Post a Comment