Tuesday, January 15, 2013

Functional testing in SOA environment

My first project in a service oriented environment has reached functional testing stage. The product involves processing large amounts of business objects whose reference data is obtained from other subsystems through service calls, in production.

While devising the strategy for functional testing, the issue was how to get this reference data in a test environment.

Unit Testing

During unit testing, the service calls were mocked using a mocking library (Mockito in our case), and the expected responses were returned using when - thenReturn construct, as follows:

 

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.mockito.MockitoAnnotations;
import org.mockito.Mock;
import org.junit.Before;
import org.junit.Test;

public class SweepTest 
{
    /*
     * Specify that the service wrapper should be mocked.
     */
    @Mock
    private XServiceWrapper xServiceWrapper;

    /*
     * SomeBusinessObjectManager has reference to XServiceWrapper and we want the 
     * mocked instance to be injected instead of the normal implementation.
     */
    @InjectMocks
    @Autowired
    private SomeBusinessObjectManager boManager;

    @Before
    public void setup()
    {
         MockitoAnnotations.initMocks(this);

         /*
          * Mock the response object from the service.
          */
         XServiceResponse serviceResponse = mock(XServiceResponse.class);
         when(serviceResponse.getXValue()).thenReturn(myValue);

         /*
          * Mock the service call and return mocked response object.
          */
         when(xServiceWrapper.serviceCall()).thenReturn(serviceResponse);
    }
}



Functional Testing

During functional testing, we identified golden set of use cases that necessarily and sufficiently cover the behavior of the system in various scenarios. The data for various internal objects for these golden set of use cases is prepared. But, it is not feasible to get relevant data from the external services that would suit the input data for a given use case. So, the following changes were done to the project w.r.t interacting with external services:
  1. Define an ExternalServiceFacade interface that would define all the calls to be made to different external services.
  2. 
    /**
     * ExternalServiceFacade.java
     * 
     * $Source$
     */
    /*
     * Copyright (c) 2013 Krovi.com, Inc. All rights reserved.
     */
    
    package com.krovi.app.manager;
    
    import com.krovi.services.BusinessObjectA;
    import com.krovi.services.BusinessObjectB;
    import com.krovi.services.BusinessObjectC;
    
    /**
     * This class follows the Facade pattern and provides APIs to
     * interact with various external services that provide reference data for the given object IDs.
     * 
     * @author krovi
     * @version $Id$
     */
    public interface ExternalServiceFacade
    {
        BusinessObjectA lookupBusinessObjectA(String referenceId);
    
        BusinessObjectB lookupBusinessObjectB(String referenceId1, String referenceId2);
    
        BusinessObjectC lookupBusinessObjectC(String referenceId1, String referenceId2, Long referenceValue3);
    
    }
    
    
    
  3. Provide an implementation that delegates the calls to the actual services and obtains the responses. This implementation will be used in production scenarios.
  4. 
    /**
     * ExternalServiceFacadeImpl.java
     * 
     * $Source$
     */
    /*
     * Copyright (c) 2013 Krovi.com, Inc. All rights reserved.
     */
    
    package com.krovi.app.manager;
    
    import com.krovi.services.BusinessObjectA;
    import com.krovi.services.BusinessObjectAService;
    import com.krovi.services.BusinessObjectAServiceClient;
    import com.krovi.services.BusinessObjectB;
    import com.krovi.services.BusinessObjectBService;
    import com.krovi.services.BusinessObjectBServiceClient;
    import com.krovi.services.BusinessObjectC;
    import com.krovi.services.BusinessObjectCService;
    import com.krovi.services.BusinessObjectCServiceClient;
    
    /**
     * @author krovi
     * @version $Id$
     */
    public class ExternalServiceFacadeImpl implements ExternalServiceFacade
    {
        @Autowired
        private BusinessObjectAService boAService;
    
        @Autowired
        private BusinessObjectBService boBService;
    
        @Autowired
        private BusinessObjectCService boCService;
    
        @Override
        public BusinessObjectA lookupBusinessObjectA(String referenceId)
        {
            BusinessObjectAServiceClient client = boAService.getClient();
            return client.lookupBusinessObjectA(referenceId);
        }
    
        @Override
        public BusinessObjectB lookupBusinessObjectB(String referenceId1, String referenceId2)
        {
            BusinessObjectBServiceClient client = boBService.getClient();
            return client.lookupBusinessObjectB(referenceId1, referenceId2);
        }
    
        @Override
        public BusinessObjectC lookupBusinessObjectC(String referenceId1, String referenceId2, Long referenceValue3)
        {
            BusinessObjectCServiceClient client = boCService.getClient();
            return client.lookupBusinessObjectC(referenceId1, referenceId2, referenceValue3);
        }
    
    }
    
    
    
  5. Provide a mock implementation that will connect to a local data store that is populated with the expected data and returns it. This implementation will be used in functional testing scenario.
  6.  
    /**
     * MockExternalServiceFacadeImpl.java
     * 
     * $Source$
     */
    /*
     * Copyright (c) 2013 Krovi.com, Inc. All rights reserved.
     */
    
    package com.krovi.app.manager;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    import com.krovi.services.BusinessObjectA;
    import com.krovi.services.BusinessObjectB;
    import com.krovi.services.BusinessObjectC;
    
    /**
     * @author krovi
     * @version $Id$
     */
    public class MockExternalServiceFacadeImpl implements ExternalServiceFacade
    {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public MockExternalServiceFacadeImpl()
        {
            setupDBObjects();
        }
    
        private void setupDBObjects()
        {
            /**
             * 1. This method creates DB tables corresponding to the structures of BusinessObject* classes. 2. The test
             * harness would insert test data into these tables as per the use cases.
             */
        }
    
        @Override
        public BusinessObjectA lookupBusinessObjectA(String referenceId)
        {
            // Look up the database instead of making a service call. The database would contain the appropriate object
            // required for the test case that is being currently run.
            return null;
        }
    
        @Override
        public BusinessObjectB lookupBusinessObjectB(String referenceId1, String referenceId2)
        {
            // Look up the database instead of making a service call. The database would contain the appropriate object
            // required for the test case that is being currently run.
            return null;
        }
    
        @Override
        public BusinessObjectC lookupBusinessObjectC(String referenceId1, String referenceId2, Long referenceValue3)
        {
            // Look up the database instead of making a service call. The database would contain the appropriate object
            // required for the test case that is being currently run.
            return null;
        }
    
    }
    
    
  7. Modify all calls to services in the entire code base to go through ExternalServiceFacade.
  8. The spring configuration files for production and testing stages are modified to inject the appropriate implementation of the ExternalServiceFacade.

No comments :

Post a Comment