Class MockRegister

java.lang.Object
com.avoka.tm.test.MockRegister

public class MockRegister extends 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:

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:

  1. Unit test code registers mock response for an external service call.
  2. Unit test code then calls service's invoke method.
  3. The invoked service now calls an external service.
  4. Transact finds a matching registered mock request object and returns the associated mock response object.
  5. Service code receives mock response object and performs its logic.
  6. 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": "[email protected]"
         ]

         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
     
  • Method Summary

    Modifier and Type
    Method
    Description
    static void
    Clear the Mock Register of all mock values.
    Return the HttpResponse registered for the given request, if any.
    static Object
    getServiceResult(String serviceName, com.avoka.core.util.ver.Version version, boolean isCurrentVersion, String clientCode)
    Return the pair of SvcDef/result object registered for the given service, if any.
    static Object
    getServiceResult(String serviceName, com.avoka.core.util.ver.Version version, boolean isCurrentVersion, String clientCode, Map paramsMap)
    Return the pair of SvcDef/result object registered for the given service, if any.
    static boolean
    hasServiceResult(String serviceName, com.avoka.core.util.ver.Version version, boolean isCurrentVersion, String clientCode)
    Check if there is a registered entry for service.
    static boolean
    hasServiceResult(String serviceName, com.avoka.core.util.ver.Version version, boolean isCurrentVersion, String clientCode, Map paramsMap)
    Check if there is a registered entry for service.
    Resolves the previously set HttpRequest (via when(HttpRequest) and registers the given HttpResponse for it in the mock test request registry.
    thenReturn(Object serviceResult)
    Resolves the previously set SvcDef (via when(SvcDef) and registers the given Object for it in the mock test service registry.
    when(HttpRequest request)
    Set the mock test context request registry key (the HttpRequest).
    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 Details

    • MockRegister

      public MockRegister()
  • Method Details

    • 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(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 Object getServiceResult(String serviceName, com.avoka.core.util.ver.Version version, boolean isCurrentVersion, 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 Object getServiceResult(String serviceName, com.avoka.core.util.ver.Version version, boolean isCurrentVersion, String clientCode, 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(String serviceName, com.avoka.core.util.ver.Version version, boolean isCurrentVersion, 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(String serviceName, com.avoka.core.util.ver.Version version, boolean isCurrentVersion, String clientCode, 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.