Class 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 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​(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.