Skip to main content

IBM WebSphere Developer Technical Journal: Reliable and repeatable unit testing for Service Component Architecture modules -- Part 2

Create repeatable tests for SCA modules that implement business processes

David J.N. Artus (artusd@uk.ibm.com), Consulting IT Specialist, IBM
David Artus
David Artus is member of the IBM Software Services for WebSphere team working out of the IBM Hursley Lab in the UK. He has provided WebSphere consultancy and mentoring since he joined IBM in 1999. Prior to joining IBM David worked in a variety of industries including Investment Banking, Travel and IT Consultancy. His interests include design of distributed systems, object technologies, and service-oriented architectures.

Summary:  Repeatable unit testing provides an efficient and reliable means of verifying the quality of solution components. This article describes issues you may encounter testing Service Component Architecture (SCA) modules that implement business processes using Business Process Execution Language (BPEL), and how using mock objects can make repeatable testing of these components a reality.

Date:  20 Sep 2006
Level:  Intermediate
Activity:  547 views

From the IBM WebSphere Developer Technical Journal.

Introduction

In Part 1 of this series on component testing, we described an approach to performing repeatable testing of Service Component Architecture (SCA) components using the JUnit open source framework and Cactus, its server-side extension, and we showed how a unit test suite for a component can be defined in a set of test specification files with optional payload files. Figure 1 shows the major test specification parts for a service that takes a simple PostCode string and returns a data object, the expected result being specified in a simple payload file.


Figure 1. Example test specification
Figure 1. Example test specification

Since Eclipse JUnit execution facilities are delivered with WebSphere Integration Developer, running a set of tests is simple. The result is a visual display, like the one shown in Figure 2.


Figure 2. JUnit test execution
Figure 2. JUnit test execution

In this article, we will describe some issues you might come across when testing SCA components that implement business processes using Business Process Execution Language (BPEL), then discuss how to address these issues by using mock objects. We will also present some techniques for creating mock objects quickly. This article is accompanied by a download file with code examples that implement these techniques.

If you plan to work through the examples presented in this second article, you will find it helpful to work through the previous article first.


Testing BPEL

Since the WebSphere Process Server programming model exposes business processes as SCA components that can be invoked in the same manner as any other SCA component, we might then expect to be able to test BPEL SCA components in the same manner as any other component. However, we find that there are some issues with such as approach. Consider the very simple, example process shown in Figure 3.


Figure 3. Simple BPEL process
Figure 3. Simple BPEL process

This example demonstrates the three key problematic issues:

  1. The business process uses a human task, and the result of the human task controls some key execution paths.
  2. External services may not be readily executable in unit test environments; for example, they may depend upon infrastructure not available to the developers.
  3. The business process is initiated but there is no corresponding reply.

Let's look at each of these issues in more detail.

1. Human task

The use of human tasks is extremely common is real world business process implementations. Clearly, if we are to implement a repeatable set of tests that can be executed automatically, then we must find a way to remove the need for human intervention. Further, our example shows a very common pattern: the outcome of the human task determines which code path is taken. In Figure 3, you can see that the choice between InvokeAgreedPayment and InvokeCaseClosed depends upon the human response.

If our tests are to achieve good code coverage, then we must be able to control the result of human tasks. Hence, the need for the following requirement:

It must be possible to simulate the actions of humans in response to different input data.

2. External services

External services present a number of challenges in addition to the previously noted one-way response issues:

  • Some services (for example, those implemented using mainframe or ERP systems) may not be available to developers in unit test environments.
  • Even when such systems are available, the test data may not be suitable for exercising all code paths; that is, it may be difficult to simulate some error conditions.
  • In many cases, the development of services occurs in parallel with the development of business processes. Hence, there is no guarantee that a service implementation will be delivered when it is needed for unit testing.

These factors lead to another testing requirement:

It must be possible to simulate the actions of services in response to different input data.

3. One-way invocation

Until now, all our SCA tests have had the pattern invoke service, validate response. Our example business process, in common with many real world business processes, has no response. We can readily construct a unit test to invoke the BPEL process, but there is no response to validate, so how can we determine its success or failure? Our determination of success or failure must lie in the ability to determine whether actions such as invoking other services have been performed correctly.

If our example process is correctly implemented, then particular values of input data will lead to the service invocation InvokeAutoSettlement while other input values will lead to the creation of a human task and, subsequently, other service invocations.

This leads to the testing requirement:

It must be possible to determine that a service invocation has occurred.

We also note that there are assignment statements prior to the two service invocations, which may be arbitrarily complex. This leads to another requirement:

It must be possible to determine that the correct data was used to invoke a service.

There is one further requirement, this one being particularly important when the BPEL process has more complex logic. In our simple example, it's clear that if InvokeAutoSettlement is used, then (for example) InvokeAgreedPayment will not be used. A unit test should be able to determine that such mutual exclusivity has been correctly implemented. Therefore:

It must possible to determine that a service has not been invoked.

Before we consider how such testing requirements can be satisfied, let us consider some additional issues.


Mock objects and mock object controllers

We have established the need to emulate SCA components such as human tasks and external services so that they can drive particular code paths in the BPEL process. This is accomplished with the common testing pattern known as mock objects.

Implementing such mock object services in WebSphere Integration Developer is comparatively simple. Our starting point would be a real component interface, such as that shown in Figure 4.


Figure 4. Customer interface
Figure 4. Customer interface

WebSphere Integration Developer enables us, with a few mouse clicks, to create a new SCA component that implements that interface, as shown in Figure 5.


Figure 5. Mock customer
Figure 5. Mock customer

With a further mouse-click, we can then generate a skeletal implementation of the interface:

public class MockCustomerImpl {
	// ... constructor etc. removed ...

	public DataObject getCustomer(Integer customerId)
			throws ServiceBusinessException {
		//TODO Needs to be implemented.
		return null;
	}

}

We can then complete the creation of a functioning mock object by adding code to construct, populate, and return a suitable data object. In principle, creating such code is not particularly difficult, but can be somewhat tedious and error prone to write, especially if the data object is large and contains nested objects and arrays. This is a very similar issue to that we encountered in the previous article when performing test invocations. There, we needed to construct potentially complex payload objects, and our solution was to externalise the payload data to XML files. Here, we will show how to apply the same ideas to mock object return values, externalising the return values to XML files.

With mock objects, we can replace human tasks and service components to create a reproducible test harness for our BPEL processes; we will be able to execute the processes without human intervention and with no dependency on external systems.

So, we have addressed some of the test requirements: we can actually execute the tests. The remaining issues are concerned with validating that a test was successful. To do this, we need to verify that:

  • Specific services were invoked with the correct data.
  • Specific services were not invoked.

We accomplish this by extending the capabilities of the mock object, adding a second interface (Figure 6).


Figure 6. Mock controller interface
Figure 6. Mock controller interface

In this interface, you see four operations. The loadData operation is used in our scheme for externalising return data, which we will discuss later. The remaining three operations resetLastRequest, checkNoRequest, and checkExpectedResult enable the validation of service invocation.

Our mock object will then implement both the service interface of the component it is replacing, plus the controller interface. An outline of a mock object implementation would look like:

public class MockServiceImpl {
	// ... constructor etc. removed ...

	private DataObject lastRequest = null

	// the service method used by the BPEL process under test
	public void exampleServiceMethod(DataObject requestData)
			throws ServiceBusinessException {
		lastRequest = requestData;
	}

	public void resetLastRequest(String label)
			throws ServiceBusinessException {

		lastRequest = null;
	}

	public String checkNoRequest()
		throws ServiceBusinessException {

		assertIsNull(lastRequest);

	}

	public String checkExpectedRequest(DataObject expectedValue)
			throws ServiceBusinessException {

		// code to compare expectedValue with lastRequest
	}
}

A test of a BPEL process that uses the exampleServiceMethod could consist of three steps, each specified in its own .tspec file:

  1. Invoke the mock controller resetLastRequest.
  2. Initiate the BPEL process with values intended to drive the BPEL to a path invoking exampleServiceMethod with certain values.
  3. Invoke the mock controller checkExpectedRequest, passing the expected values; if the expected values are not found, an exception is raised by the mock controller and the test will fail.

Similarly, a second test sequence can be defined to verify that exampleServiceMethod was not invoked:

  1. Invoke the mock controller resetLastRequest.
  2. Initiate the BPEL process with values intended to drive the BPEL to a path not invoking exampleServiceMethod.
  3. Invoke the mock controller checkNoRequest; if lastRequest is not null (implying that exampleServiceMethod was unexpectedly called), then an exception is raised by the mock controller and the test will fail.

The scheme we describe here is simplistic and intended to demonstrate the principles of using a mock controller interface in conjunction with a mock object. This scheme will not be sufficient for more demanding scenarios such as:

  • Multi-user scenarios where many mock object instances are servicing many requests.
  • A BPEL process makes several different calls to the same mock object in rapid succession.

Such scenarios expose the need for a more sophisticated scheme, than simply keeping and checking the most recent request data. In the remainder of the article, we will show how to implement our simplistic mock object scheme, exploiting a set of libraries and components that greatly reduce the work required. If your test scenarios require a more complex mock controller implementation, the structure described here will provide a good starting point.


Test scenario

Our test scenario is part of a fictional archive application managing a long-term store of items. We will be working on the business process by which a customer requests that an item be retrieved and is later notified that the retrieval has completed.

You can download a Project Interchange file containing all the code for this example. If you want to work through this article, then import these projects:


Table 1. Projects to import
ProjectTypeContents
L_ArchiveSCA libraryInterface for the Archive application components. Delivered as part of the application, used by JUnit tests.
MP_ArchiveRetrievalSCA moduleThe Archive application process module. Contains the BPEL process we are testing.
J_ScaUtilitiesJava libraryUtilities usable by both applications and tests.
LT_ScaTestSCA libraryThe interface used by unit tests and mock objects, not for application use.
LT_ScaJunitTestSCA libraryThe JUnit SCA test execution code. For use only by JUnit tests.
MT_TestArchiveRetrievalSCA moduleModule delivering JUnit tests
MT_TestArchiveRetrievalJUnitWebJ2EE Web projectThe JUnit test code, test definitions, and payload data, part of MT_TestArchiveRetrieval.

If you open the Assembly diagram for the MP_ArchiveRetrieval module, you will see the interfaces offered by the retrieval process and services it uses.


Figure 7. Archive Module Assembly
Figure 7. Archive Module Assembly

Process interfaces

We see that the process has an initiation interface (Figure 8).


Figure 8. Initiation interface
Figure 8. Initiation interface

This interface might reasonably be accessed by a customer via some suitable user Interface. The process also has a second interface, by which the process is notified of the completion of a retrieval request (Figure 9).


Figure 9. Retrieval result notification interface
Figure 9. Retrieval result notification interface

Services used by retrieval process

The business process uses two services. The Customer service will retrieve customer information given the customer's unique ID (Figure 10).


Figure 10. Customer service interface
Figure 10. Customer service interface

When the retrieval is completed then the process will notify the Customer via the CustomerCommunication service (Figure 11).


Figure 11. CustomerCommunication service
Figure 11. CustomerCommunication service

Required mock objects

The projects you imported did not include implementations of the Customer and CustomerCommunication services. We will create mock objects for these two services later in this article. (If you wish to skip the work of creating the mock objects, you can import the projects MT_MockCustomer and MT_MockCustomerCommunition from the Project Interchange file.)

The retrieval process

Before we start work on the mock objects, you might like to examine the business process as it is currently implemented. Open the process to see the overall structure, shown Figure 12.


Figure 12. Retrieval process
Figure 12. Retrieval process

At this stage, we are primarily interested in the three items highlighted in red:

  • The Receive statement labelled Initiation: this is the target of our unit test initiation.
  • The Invoke statements labelled GetCustomerDetails: we will look at this in more detail shortly, but note that this is one point where a mock object will be invoked.
  • The Invoke statement labelled NotifyCustomer: this is our second mock object invocation point.

You might also notice that this is far from a complete implementation of the process; for example, there is no actual access to the archive system. We show this abbreviated example for ease of understanding; however, it is worth reflecting on two points:

  • Our preference is to build and test large BPEL processes incrementally, focusing on particular parts of the overall process. Our repeatable tests give us confidence should any re-factoring become necessary.
  • An important principle, especially relating to performance testing, is "Test Early, Test Often." We would wish to take even a skeletal process like this example into integration test and performance test environments. Early experiments give immediate insights into stability, error handling, and performance characteristics.

You can also examine the fault handlers for the GetCustomerDetails invoke, noting that notFound faults and transientError faults are potentially treated differently (Figure 13).


Figure 13. Faults on GetCustomerDetails
Figure 13. Faults on GetCustomerDetails

In this simplified code, we have no special treatment of the different faults; however, in a real implementation, we would need to exercise these different paths. When we implement our mock object, we will need to consider how to produce the different errors needed to drive the different paths.


The unit tests

To examine the unit tests that we intend to run, open a Web perspective in WebSphere Integration Developer. In the Project Explorer view, expand Dynamic Web Projects => MT_TestArchiveRetrievalJunitWeb => Java Resources => Java Source => com.ibm.issw.archive.utsuite => ArchiveTest-Data => testArchiveInitiation (Figure 14).


Figure 14. Unit test specifications
Figure 14. Unit test specifications

The ArchiveInitiation test is comprised of five steps. Open each tspec file in turn to understand how the test proceeds:

  • 010-InitialiseCustomers resets the Customer mock object.
  • 020-InitialiseNotification resets the CustomerCommunication mock object.
  • 030-LoadCustomers initialises the Customer mock object with some customer data. (We will explain how this works shortly.)
  • 040-InitiateRetrieval starts the business process itself. The expectation is that the process will use GetCustomerDetails to obtain data from the Customer mock object and then use the NotifyCustomer features of the CustomerCommunication mock object.
  • 050-CheckNotification will then verify that the CustomerCommiunication mock object was called with the correct data.

You will also see another set of tests owned by CustomerTest.java, in the CustomerTest-Data directory (Figure 15). These tests enable us to exercise the Customer mock object directly. These are supplied to enable a quick demonstration of the mock object we are about to build next.


Figure 15. Customer tests
Figure 15. Customer tests

We will describe the purpose of these tests when we are ready to run them, after we have completed building the Customer mock object.


Customer mock object

To create the Customer mock object, we will perform these general steps:

  1. Create the mock Customer module
  2. Create mock Customer component
  3. Generate an implementation class
  4. Implement loadData operation
  5. Implement getCustomerDetails operation
  6. Test the mock object

A. Create the mock Customer module

In these examples, we create separate modules for each mock object. For a realistic development scenario, this does result in the creation of a quite a number of modules and WebSphere Integration Developer projects, but we find the benefits of separation of concerns outweigh the increase in complexity.

To create the module:

  1. In the Business Integration perspective, Business Integration view, right-click and select New => Module to display the New Module dialogue.

  2. Enter the module name MT_MockCustomer and click Finish.

  3. The mock object must implement the Customer interface from the L_Archive library. We will also exploit some libraries from our test framework. Therefore, we need to set up the necessary dependencies. Double-click on the newly created project to bring up the Dependency editor.

  4. In the Libraries section, click Add to bring up the Library Selection dialogue.

  5. Select L_Archive and click OK.

  6. Similarly, add dependencies on J_ScaUtilities, L_ScaTest (Figure 16).



    Figure 16. Add libraries
    Figure 16. Add libraries

    Note that mock objects do not themselves use JUnit, and therefore we do not need to add dependency on the LT_ScaJUnitTest library.

  7. Use File => Save and File => Close to save your changes and close the dependency editor, respectively.

B. Create mock Customer component

  1. Select the MT_MockCustomer Assembly Diagram and double-click to open the editor.

  2. In the palate, select the uppermost element, and from the sub-palate, select Component (with no implementation type), then click on the editing surface. This creates the mock object component. (Figure 17)



    Figure 17. Mock Customer assembly
    Figure 17. Mock Customer assembly

  3. Right-click the newly created component. Rename the component MockCustomer. The primary role of the Mock Customer component is to provide the CustomerService interface.

  4. Select Add Interface from the context menu to bring up the Add Interface dialogue.

  5. Select I_CustomerService and click OK. You can see the result in the Details tab of the Properties view (Figure 18).



    Figure 18. Mock Customer with service interface
    Figure 18. Mock Customer with service interface

C. Generate an implementation class

To create a skeletal implemention of the mock Customer component:

  1. Right-click on the component and select Generate Implementation to bring up the Generate Implementation dialogue.

  2. This dialogue enables you to specify the Java package for the implementation class, offering names derived from the interface namespaces. It is a common convention to use package names that conform to your own naming convention. To do this, click ,b.New Package, and then enter a suitable name. We use com.ibm.issw.archive.mock.customer (Figure 19). Click OK.



    Figure 19. Implementation package
    Figure 19. Implementation package

  3. Select the newly created package and click OK again to generate the implementation code (Figure 20).



    Figure 20. Generated implementation
    Figure 20. Generated implementation

  4. Now, select Add Interface again and add the I_MockServiceController interface. Notice that we have generated the service implementation before adding the second interface; we will be using a library implementation of the mock controller interface, so we don't need to write any code for the second interface.



    Figure 21. Mock object interface
    Figure 21. Mock object interface

  5. You can see the result in the Details tab of the Properties view (Figure 22).



    Figure 22. Mock Customer interfaces
    Figure 22. Mock Customer interfaces

  6. Two other projects, MT_TestArchiveRetrieval and M_Archive, will need to use this mock component, so export the interfaces to give those projects access. Right-click the MockCustomer component, and select Generate Export => SCA Binding to bring up the Select Interface dialogue (Figure 23).

  7. Check both interfaces, then OK.



    Figure 23. Export mock Customer interfaces
    Figure 23. Export mock Customer interfaces

    Your assembly diagram will now look like Figure 24.



    Figure 24. Mock Customer export
    Figure 24. Mock Customer export

  8. Save the Assembly Diagram. We are now ready to provide implementations for the interfaces.

D. Implement loadData operation

Now that we have added the mock Service Controller interface to the MockCustomer component, you will see an error because we have no implementation of the required operations (Figure 25).


Figure 25. Implementation errors
Figure 25. Implementation errors

A suitable set of implementations is provided in the testing framework, in a base class named ScaMockBase.

  1. Modify the MockCustomerImpl.java implementation to extend this base class by adding the import of ScaMockBase and the extends clause as shown below:

    import com.ibm.swservices.sca.test.ScaMockBase;
    import com.ibm.websphere.sca.ServiceBusinessException;
    import commonj.sdo.DataObject;
    import com.ibm.websphere.sca.ServiceManager;
    
    public class MockCustomerImpl extends ScaMockBase {

  2. Save these changes. This will remove the errors.

  3. Examine the code for the ScaMockBase class.Click on the class name and press F3 to open the code. You will see a number of protected (and therefore accessible to your MockCustomer class) member variables:

    protected Logger m_logger = Logger.getLogger(this.getClass().getName());
    
    protected Object m_lastRequest = null;
    
    protected String m_lastFaultName = null;
    
    protected List m_dataList = new ArrayList();

    You will see that ScaMockBase populates m_dataList with valid customers from XML files:

    DataObject data = XmlSdoUtilities.readDataFromXmlFile(dataFiles[count]);
    m_dataList.add(data);
    

    We will be using this list in our getCustomerDetails implementation. We need to create some files containing a set of valid customer data. The format of the files is the serialised form of the Customer data object:

    <?xml version="1.0" encoding="UTF-8"?>
    <_:TestDefinition xsi:type="l:Customer"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:l="http://L_Archive"
    xmlns:_="http://scatest/issw/ibm/com">
    	<customerId>2</customerId>
    	<name>Bingley Bradford</name>
    	<email>bb@bbbs.com</email>
    	<phone>22222</phone>
    </_:TestDefinition>
    

    Remember that in our previous article we described a utility for creating an example of such files from the data object definition. The data files must be located in the mock Customer project so that they can be picked up from the class loader. We have provided some sample files, delivered in the MT_TestArchiveRetrievalJUnitWeb project.

  4. In the Web perspective, Project Explorer view, expand Dynamic Web Projects => MT_TestArchiveRetrievalJUnitWeb => Java Resources => JavaSource.

  5. Right-click on the com.ibm.issw.archive.testData.Customer folder, and select Copy.

  6. Expand Other Projects => MT_MockCustomer, then right-click com.ibm.issw.archive.mock.customer, and Paste (Figure 26).



    Figure 26. Install test data
    Figure 26. Install test data

    We have now placed the test data in a directory tree in the same location as the MockCustomer class; the test data is loaded by the classloader for this class. If you chose a different package name, you will need to adjust the location of the test data files accordingly.

  7. You now have an implementation of the loadData operation, along with test data for it to load. Open the two data files Customer001.xml and Customer002.xml and note the Customer IDs that will be loaded.

Other attributes of the ScaMockBase class

You should notice that in ScaMockBase, an instance of java.util.Logger is created, enabling us to add trace and error statements to our implementation:

protected Logger m_logger = Logger.getLogger(this.getClass().getName());

Also, m_lastRequest and m_lastFaultName are used to implement the Last Request scheme we described earlier. We will be using these later in our second mock object implementation.

E. Implement getCustomerDetails operation

We can now return to MockCustomerImpl.java and add the implementation of getCustomerDetails.

  1. Use the following code as the body of that method:

    m_logger.fine("getCustomer " + customerId);
    
    for (int i = 0; i < m_dataList.size(); i++){
    DataObject candidate = (DataObject) m_dataList.get(i);
    
    int candidateId = candidate.getInt("customerId");
    	m_logger.finest("candidate " + candidateId);
    
    	if ( candidateId == customerId.intValue()){
    		m_logger.finest("returning candidate ");
    		return candidate;
    	}
    }
    throw new ServiceBusinessException("NotFound");
    

    Observe that the implementation emits trace events using the m_logger member variable from the base class.

  2. Save your implementation.

F. Test the mock object

You now have a functioning mock object that provides the getCustomerDetails method and a set of tests for that object. You can now deploy the MockCustomer module and the tests to a server, and then verify that the MockCustomer operates properly.

  1. Wire the test module to the mock service module by expanding the MT_TestArchiveRetrievalApp module, and open its assembly diagram. You will see the standalone references used by the tests.

  2. Select the customer standalone reference and, in the Properties view, select the Binding tab.

  3. Click on Browse to display the SCA Export Selection dialogue. Select the export provided by the MockCustomer module. (Figure 27)



    Figure 27. Wire MockCustomer to test
    Figure 27. Wire MockCustomer to test

  4. In the same way, wire I_MockServiceController to the same module. We now have enabled the tests to use both interfaces to our mock object.



    Figure 28, Wire MockCustomer Controller to test
    Figure 28, Wire MockCustomer Controller to test

    Now we can deploy the modules to the server.

  5. In the Business Integration perspective, Servers view, right-click the WPS server, and select Add Remove Projects to bring up the Add and Remove Projects dialogue.

  6. Add MT_TestArchiveRetrievalApp and MT_MockCustomerApp to the server, then click Finish.



    Figure 29. Deploy MockCustomer for testing
    Figure 29. Deploy MockCustomer for testing

  7. Wait for the application deployment and initiation to complete and then execute the test. In the Web perspective, expand MT_TestArchiveRetrievalApp and select CustomerTest.java.

  8. From the Run menu select Run... to display the Run dialogue.

  9. Select JUnit and click New.



    Figure 30. Run MockCustomer test
    Figure 30. Run MockCustomer test

    This will create a new run configuration, CustomerTest, as shown in Figure 31.



    Figure 31. Run configuration
    Figure 31. Run configuration

  10. Add the Cactus URL specification:

    -Dcactus.contextURL=http://localhost:9080/MT_TestArchiveRetrievalJunitWeb

    in the Arguments tab (adjust the host and port to correspond to your test environment).

  11. Click Run to launch the test.


Figure 32. Cactus argument and execution
Figure 32. Cactus argument and execution

You should see the JUnit test succeed, plus some information displayed in the console showing the data comparisons performed by the test (Figure 33).


Figure 33. Test success
Figure 33. Test success

Figure 34. Test progress in console
Figure 34. Test progress in console

Take the time to study the test files. You will see:

  • 01-InitialiseCustomers, which resets the list of customers held by MockCustomer.

  • 02-LoadCustomers, which reads the test data we installed. Notice how the location of the test data is specified in the payload:

    <service>I_MockServiceControllerPartnerRef</service>
    	<method>loadData</method>
    	<payloadString>testData/Customer</payloadString>

  • 03-GetCustomer, which retrieves a customer with a specified ID and compares the details with the contents of the expected results file ExpectedCustomer.xml.

  • 04-GetBadCustomer, which attempts to retrieve a customer with an invalid ID and expects a specific fault to be thrown.

This test demonstrates that our MockCustomer is able to provide different responses that could drive our business process to different paths.


Mock CustomerCommunication object

We will illustrate the second aspect of mock objects by constructing a MockCustomerCommunication object. In our business process, the CustomerCommunication component should notify the Customer about the results of its retrieval request. In testing the business process, we want to determine that the Communication component is invoked with the correct data. We will enable such testing by creating a suitable mock object.

Creating the MockCustomerCommunication component

Creating MockCustomerCommunication uses the same techniques you just followed for the MockCustomer object. We will outline the steps here, but detailing only new information; refer to the detailed instructions and screenshots above, as applicable.

  1. Create an MT_MockCustomerCommunication module, adding dependencies on the L_Archive, J_ScaUtilities and L_ScaTest libraries.

  2. Create MockCustomerCommunication component and implementation, following these additional steps:

    • Add the I_CustomerCommunicationService interface to the component.
    • Generate a skeletal implementation in a package com.ibm.issw.archive.mock.customercommunication.
    • Add the I_MockServiceController.
    • Generate an export for the two interfaces with an SCA binding.
    • Modify the implementation to extend the ScaMockBase class, thus implementing the I_MockServiceController interface.
  3. Save your work so far. There should be no errors.

Implementing the service

We now need to consider the service operation that will be used by our business process.

  1. Open your implementation class and examine the sendRetrievalResult method. Observe that there is no useful implementation.

    public void sendRetrievalResult(DataObject retrievalNotifcation) {
    		//TODO Needs to be implemented.
    	}

  2. We require that this method interact with the ScaMockBase class so that the testing methods checkExpectedRequest and checkNoRequest can verify whether this method has been called with the correct data. Achieve this by modifying the code to read:

    public void sendRetrievalResult(DataObject retrievalNotifcation) {
    	m_logger.info("sendRetrievalResult" +
    		XmlSdoUtilities.getDataObjectAsXml(retrievalNotifcation));
    
    	saveLastRequest(retrievalNotifcation);
    }

  3. The trace message exploits a utility method in the class XmlSdoUtilities, so you will need to add this import statement:

    import com.ibm.swservices.sca.utilities.XmlSdoUtilities;

  4. The call to a method in ScaMockBase, namely saveLastRequest, is the only required implementation. Ensure all your changes are saved. The mock object is now complete.

Testing the BPEL process

We can now wire the BPEL process to the two mock objects we created and run the tests.

  1. In the Business Integration perspective, open the MP_ArchiveRetrieval assembly diagram and select the CustomerService import.

  2. In the Properties view, select the Binding tab and click Browse to bring up the SCA Export Selection dialogue.

  3. Select the export from your mock Customer module and click OK.

  4. Examine the values entered in the binding tab to see how they refer to your mock object module.



    Figure 35. Wire process to MockCustomer
    Figure 35. Wire process to MockCustomer

  5. In the same way, select the CustomerCommunicationService import and wire it to the MockCustomerCommunication module. The result should be as shown in Figure 36.



    Figure 36. Wire process to MockCustomerCommunication
    Figure 36. Wire process to MockCustomerCommunication

  6. Save the modified assembly diagram.

  7. We also need to enable the tests to invoke the mock object controllers, so open the MT_TestArchiveRetrieval assembly diagram and use the technique described above to ensure that the imports are correctly wired as summarised here:


Table 2. Wire tests to modules
ImportModule nameExport name
I_MockServiceControllerPartnerMT_MockCustomerMockCustomerExport
I_MockCustomerCommunicationControllerPartnerMT_MockCustomerCommunicationMockCustomerCommunicationExport
archiveRetrievalMP_ArchiveRetrievalInitiation
customer (we only use this import if running the Mock Customer test)MT_MockCustomerMockCustomerExport
  1. Now we are ready to initiate the test. Ensure that your test server is started and add the four projects to it:

    • MP_ArchiveRetrieval
    • MT_TestArchiveRetrieval
    • MT_MockCustomer
    • MT_MockCustomerCommunication.
  2. In the Web perspective, expand MT_TestArchiveRetrieval and select ArchiveTest.java. Select Run => Run... to create a JUnit launch configuration, remembering to specify the VM argument:

    -Dcactus.contextURL=http://localhost:9080/MT_TestArchiveRetrievalJunitWeb



    Figure 37. Launch archive test
    Figure 37. Launch archive test

  3. The JUnit test should complete with no errors. Examine the test specifications to remind yourself of how the tests and mock objects cooperate to verify the process behaviour.


Conclusion

We have seen how simple mock objects can enable repeatable unit testing of a BPEL process, and described a simple framework that greatly eases the construction of mock objects. Applying the principles of repeatable unit testing to BPEL development increases both productivity and quality.


More in this series



Download

DescriptionNameSizeDownload method
Sample componentsBpelUnitTesting-v1.1.zip698 KBHTTP

Information about download methods


Resources

About the author

David Artus

David Artus is member of the IBM Software Services for WebSphere team working out of the IBM Hursley Lab in the UK. He has provided WebSphere consultancy and mentoring since he joined IBM in 1999. Prior to joining IBM David worked in a variety of industries including Investment Banking, Travel and IT Consultancy. His interests include design of distributed systems, object technologies, and service-oriented architectures.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, SOA and Web services
ArticleID=160623
ArticleTitle=IBM WebSphere Developer Technical Journal: Reliable and repeatable unit testing for Service Component Architecture modules -- Part 2
publish-date=09202006
author1-email=artusd@uk.ibm.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers