public class MockRegister
extends java.lang.Object
The MockRegister class supports testing your service code with mock responses
for external services calls when executing in a unit test context. The
MockRegister supports mocking responses for calls of the following classes:
HttpRequest
GroovyServiceInvoker
FluentFuncInvoker
ServiceInvoker
Test mocking is an important practice in modern software development as it
enables: test service code in isolation from other components developing your
service code even before other services are available testing error handling
modes which are difficult to achieve when calling real services enables testing
in environments where external services may not be available, e.g. dev machine
or a CI server enables build/CI tests to execute much faster than when calling
external services The way MockRegister works is that it registers mock response
object in a thread local store which Transact will return when executing
services code in a unit test context. The way this work is: Unit test code
registers mock response for an external service call. Unit test code then calls
service's invoke method. The invoked service now calls an external service.
Transact finds a matching registered mock request object and returns the
associated mock response object. Service code receives mock response object and
performs its logic. Unit test code verifies that service code performed
correctly for the given execution context. To illustrate this see the concrete
example HTTP service code below.
Mocking HTTP Calls
In this example we are developing a GetAccounts service which makes a HTTP
request to an retrieve a list of accounts. The HTTP endpoint configuration
details are provide by the services SvcConn
object. If the HTTP
call return a non 200 (OK) status code then our service will return a validation
error to the form.
public class GetAccounts {
public Logger logger
FuncResult invoke(FuncParam param) {
SvcConn svcConn = param.svcDef.svcConn
HttpResponse response = new GetRequest(svcConn.endpoint)
.setBasicAuth(svcConn.username, svcConn.password)
.execute()
FormFuncResult result = new FormFuncResult()
if (response.isStatusOK()) {
result.data.accounts = response.getTextContent()
} else {
result.errors.add(new ValidationError("accountsSvcError"))
}
return result
}
}
Now to test our services handling of HTTP errors we will mock a
HttpResponse
object returning a HTTP 501 Service Unavailable error. This response object is
registered using the same
GetRequest
object that is used in the service call. Transact's mock
object matching rules are discussed later.
public class GetAccountsTest extends TransactSDKUnitTest {
@Test
void testFunction() throws Exception {
FuncParam funcParam = new MockVoBuilder().createFuncParam(TRIGGER_FORM_UPDATE, svcDef, testParams)
// Create a matching GetRequest object
SvcConn svcConn = svcDef.svcConn
HttpRequest matchRequest = new GetRequest(svcConn.endpoint).setBasicAuth(svcConn.username, svcConn.password)
// Create a mock response object
HttpResponse mockResponse = new HttpResponse().setStatus(503)
// Register the request object to return mock response object when a matching HTTP request is made
new MockRegister().when(matchRequest).thenReturn(mockResponse)
// Invoke service
FormFuncResult result = (FormFuncResult) new ServiceInvoker(svcDef)
.setLogger(logger)
.invoke("funcParam", funcParam)
// Test results
logger.info result
assert result.errors.get(0).errorKey == "accountsSvcError"
}
}
The key line in the code above is the MockRegister call which registers the
HttpRequest object and associated mock response object.
new MockRegister().when(request).thenReturn(response);
When HttpRequest.execute()
it will check whether there is a
registered thread local HttpRequest object which semantically matches for
current the HttpRequest object. If there is the execute method will return the
registered mock response object immediately and not perform the actual HTTP
request.
HttpRequest Matching
When Transact is looking for an mock response object registered with a request,
it will test the registered request objects attributes against the currently
executing actual HttpRequest object. The attributes which are matched on
include: matching - request method (GET, POST, PUT, DELETE) matching - request
URI matching - request message data mock request headers is a matching set or is
a subset of actual request headers mock request parameters is a matching set or
is a subset of actual request parameters
Mocking Service / Function Calls
The other scenario where the MockRegister class is useful is when one service or
function is calling another service and we need to mock the response of the
second service or function. Service mocking is supported in the
ServiceInvoker
, FluentFuncInvoker
and
GroovyServiceInvoker
classes. To visualize this consider the
following execution path:
Trigger -> Service A
Service A -> Service Invoker
Service Invoker -> Service B
Service B -> returns response
To facilitate unit testing we use the MockRegister to skip calling Service B and
instead return a test result object directly from the ServiceInvoker class:
Test Trigger -> Service A
Service A -> Service Invoker
Service Invoker -> return mock result
To illustrate scenario this we have Prefill Function below which is calling
shared GroovyService to lookup some data from another system. The function code
is provided below:
public class PrefillFunction {
public Logger logger
FuncResult invoke(FuncParam param) {
Map params = [:]
params.request = param.request
String profileData = new GroovyServiceInvoker()
.setServiceName("LoadProfile")
.setClientCode(param.svcDef.clientCode)
.invoke(params)
Map profileMap = new GsonBuilder().create().fromJson(profileData, Map.class)
FormFuncResult result = new FormFuncResult()
result.data.profileData = profileMap;
return result
}
}
To mock the Groovy Service call profileData result in our PrefillFunction unit
test we register a mock result object with the MockRegister. Note the registered
service definition matches that used in the PrefillFunction
code.
public class PrefillFunctionTest {
public Logger logger
FuncResult invoke(FuncParam param) {
FuncParam funcParam = new MockVoBuilder()
.createFuncParam(FuncParam.TRIGGER_FORM_OPEN, Txn.FORM_SAVED, svcDef, testParams);
SvcDef matchSvcDef = new SvcDef([
"name": "LoadProfile",
"clientCode": svcDef.clientCode,
])
Map profileData = [
"firstName": "John",
"lastName": "Smith",
"email": "john.smith@maguire.com"
]
new MockRegister().when(matchSvcDef).thenReturn(profileData)
// Invoke service
FormFuncResult result = (FormFuncResult) new ServiceInvoker(svcDef)
.setLogger(logger)
.invoke("funcParam", funcParam);
// Test results
logger.info result
assert result.data != null
assert result.data.profileData == profileData
}
}
Service / Function Matching
Matching of the service or function being called is done using the registered
SvcDef attributes. The registered SvcDef should have the same attributes which
are used in the service call. These attributes include: name - the service name
(required) version - the service version number (optional) clientCode - the
service's organization client code (required)
Test Coverage
Typically your service unit test code will incorporate many
@Test
case methods to executing different scenarios to ensure you
have adequate test coverage. A multi test method unit test is provided below.
public class GetAccountsTest extends TransactSDKUnitTest {
@Test
void testOk() throws Exception {
// Test normal service call
...
}
@Test
void testBadRequest() throws Exception {
// Test HTTP 400 bad request
...
}
@Test
void testServiceUnavailable() throws Exception {
// Test HTTP 503 service unavailable
...
}
}
- Since:
- 5.1.7
Constructor Summary
Constructors
Constructor |
Description |
MockRegister() |
|
Method Summary
All Methods Static Methods Instance Methods Concrete Methods
Modifier and Type |
Method |
Description |
static void |
clear() |
Clear the Mock Register of all mock values.
|
static HttpResponse |
getHttpResponse(HttpRequest request) |
Return the HttpResponse registered for the given request, if any.
|
static java.lang.Object |
getServiceResult(java.lang.String serviceName,
com.avoka.core.util.ver.Version version,
boolean isCurrentVersion,
java.lang.String clientCode)
|
Return the pair of SvcDef/result object registered for the given
service, if any.
|
static java.lang.Object |
getServiceResult(java.lang.String serviceName,
com.avoka.core.util.ver.Version version,
boolean isCurrentVersion,
java.lang.String clientCode,
java.util.Map paramsMap)
|
Return the pair of SvcDef/result object registered for the given
service, if any.
|
static boolean |
hasServiceResult(java.lang.String serviceName,
com.avoka.core.util.ver.Version version,
boolean isCurrentVersion,
java.lang.String clientCode)
|
Check if there is a registered entry for service.
|
static boolean |
hasServiceResult(java.lang.String serviceName,
com.avoka.core.util.ver.Version version,
boolean isCurrentVersion,
java.lang.String clientCode,
java.util.Map paramsMap)
|
Check if there is a registered entry for service.
|
MockRegister |
thenReturn(HttpResponse response) |
Resolves the previously set HttpRequest (via when(HttpRequest) and
registers the given HttpResponse for it in the mock test request
registry.
|
MockRegister |
thenReturn(java.lang.Object serviceResult) |
Resolves the previously set SvcDef (via when(SvcDef) and registers the
given Object for it in the mock test service registry.
|
MockRegister |
when(HttpRequest request) |
Set the mock test context request registry key (the HttpRequest).
|
MockRegister |
when(SvcDef svcDef) |
Set the mock test context service registry key (the SvcDef).
|
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString,
wait, wait, wait
Constructor Detail
MockRegister
public MockRegister()
Method Detail
when
public MockRegister when(HttpRequest request)
Set the mock test context request registry key (the HttpRequest). This value is
used when thenReturn(HttpResponse) is called. This method in itself has no
effect on the registry. Ensure to follow it up with thenReturn(HttpResponse).
- Parameters:
request
- http request
- Returns:
- the current MockRegister instance
when
public MockRegister when(SvcDef svcDef)
Set the mock test context service registry key (the SvcDef). This value is used
when thenReturn(Object) is called. This method in itself has no effect on the
registry. Ensure to follow it up with thenReturn(Object).
- Parameters:
svcDef
- service definition
- Returns:
- the current MockRegister instance
thenReturn
public MockRegister thenReturn(HttpResponse response)
Resolves the previously set HttpRequest (via when(HttpRequest) and registers the
given HttpResponse for it in the mock test request registry.
- Parameters:
-
response
- the HttpResponse that should be returned when a
HttpRequest matching the stored request is processed.
- Returns:
- the current MockRegister instance
thenReturn
public MockRegister thenReturn(java.lang.Object serviceResult)
Resolves the previously set SvcDef (via when(SvcDef) and registers the given
Object for it in the mock test service registry.
- Parameters:
-
serviceResult
- the result object that should be returned when
ServiceInvoker.invoke is called on a service matching the stored SvcDef.
- Returns:
- the current MockRegister instance
getHttpResponse
public static HttpResponse getHttpResponse(HttpRequest request)
Return the HttpResponse registered for the given request, if any.
- Parameters:
request
- the request (required)
- Returns:
- the response registered for this request, or null if none was found.
getServiceResult
public static java.lang.Object getServiceResult(java.lang.String serviceName,
com.avoka.core.util.ver.Version version,
boolean isCurrentVersion,
java.lang.String clientCode)
Return the pair of SvcDef/result object registered for the given service, if
any. To distinguish if null is registered, please call hasServiceResult()
beforehand.
- Parameters:
serviceName
- service name
version
- service version number
isCurrentVersion
- denotes if the version is current
clientCode
- service client code
- Returns:
-
the service / result object registered for this service, or null if none was
found.
getServiceResult
public static java.lang.Object getServiceResult(java.lang.String serviceName,
com.avoka.core.util.ver.Version version,
boolean isCurrentVersion,
java.lang.String clientCode,
java.util.Map paramsMap)
Return the pair of SvcDef/result object registered for the given service, if
any. To distinguish if null is registered, please call hasServiceResult()
beforehand.
- Parameters:
serviceName
- service name
version
- service version number
isCurrentVersion
- denotes if the version is current
clientCode
- service client code
paramsMap
- service params map
- Returns:
-
the service / result object registered for this service, or null if none was
found.
- Since:
- 17.10.6
hasServiceResult
public static boolean hasServiceResult(java.lang.String serviceName,
com.avoka.core.util.ver.Version version,
boolean isCurrentVersion,
java.lang.String clientCode)
Check if there is a registered entry for service.
- Parameters:
serviceName
- service name (required)
version
- service version number
isCurrentVersion
- denotes if the version is current
clientCode
- service client code
- Returns:
- true if there is, otherwise false
hasServiceResult
public static boolean hasServiceResult(java.lang.String serviceName,
com.avoka.core.util.ver.Version version,
boolean isCurrentVersion,
java.lang.String clientCode,
java.util.Map paramsMap)
Check if there is a registered entry for service.
- Parameters:
serviceName
- service name (required)
version
- service version number
isCurrentVersion
- denotes if the version is current
clientCode
- service client code
paramsMap
- service params map
- Returns:
- true if there is, otherwise false
- Since:
- 17.10.6
clear
public static void clear()
Clear the Mock Register of all mock values.