Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "SCA/SCA Component/SCA Testing SCA Applications Unit Example"

< SCA
m (The Application to Test)
 
(17 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
== The Application to Test ==
 
== The Application to Test ==
  
Let's first begin with the description of the application we are going to test.
+
The application we are using as basis is the "Restaurant - RMI Service" application available in the examples of SCA.
It would probably have been better if we had taken the use case Restaurant given in the samples, but this example uses another "simple" application.
+
 
 +
You can find the source of this application in the SVN or by selecting '''File &gt; New &gt; Other &gt; Examples &gt; SCA &gt; SCA Examples'''.
 +
In this page, the project as been saved in the workspace as the RestaurantRMISample project.
 +
It contains the composite, the interfaces and the implementations.
  
So, we have an SCA Java project in our workspace which contains the composite, the interfaces and the implementations.
 
  
 
[[Image:SCAtestSample_applicationBasisProject.png]]
 
[[Image:SCAtestSample_applicationBasisProject.png]]
  
  
Our application is made up of three components:
+
Our application is made up of five components:
* '''HandlePurchaseComponent''' is the first component. It has one service which promoted by the composite. And it has two references toward the two next components.
+
* '''RestaurantServiceComponent''' is the first component. It has one service which is promoted by the composite. And it has two references toward '''BillServiceComponent''' and '''MenuServiceComponent'''.
* '''ClientManagerComponent''' is the second component. It has one service wired with a reference of '''HandlePurchaseComponent'''. It has no reference.
+
* '''BillServiceComponent''' is the second component. It has one service wired with '''RestaurantServiceComponent''' and has a reference toward '''VatServiceComponent'''.
* '''PurchaseProcessorComponent''' is the third component. It also has one service wired with a reference of '''HandlePurchaseComponent'''. It has no reference.
+
* '''MenuServiceComponent''' is the third component. It also has one service wired '''RestaurantServiceComponent''' and has a reference toward '''TipServiceComponent'''.
 +
* The last two components, '''VatServiceComponent''' and '''TipServiceComponent''', have each one a service and no reference.
  
  
Using the SCA Composite Designer, we got this representation.
+
Using the SCA Composite Designer, we got this diagram.
  
[[Image:SCAtestSample_applicationBusinessComposite.png]]
+
[[Image:SCAtestSample_applicationBusinessComposite.gif]]
  
  
The composite file is the following.
+
The composite source can be found in the SVN.
 +
What we are going to do is to write and run unit tests for the first component '''RestaurantServiceComponent''' in the same way SCA testing tools would allow us to do.
  
<source lang="xml">
 
<?xml version="1.0" encoding="ISO-8859-15"?>
 
<sca:composite xmlns:sca="http://www.osoa.org/xmlns/sca/1.0" name="PurchaseComposite">
 
 
  <sca:component name="HandlePurchaseComponent">
 
    <sca:implementation.java class="org.eclipse.stp.sca.example.HandlePurchaseServiceImpl"/>
 
    <sca:service name="HandlePurchaseService">
 
      <sca:interface.java interface="org.eclipse.stp.sca.example.HandlePurchaseService"/>
 
      <tuscany:binding.rmi xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0" host="localhost" port="8098" serviceName="PurchaseApplication"/>
 
    </sca:service>
 
    <sca:reference name="clientManager"/>
 
    <sca:reference name="purchaseProcessor"/>
 
  </sca:component>
 
 
 
  <sca:component name="ClientManagerComponent">
 
    <sca:implementation.java class="org.eclipse.stp.sca.example.ClientManagerServiceImpl"/>
 
    <sca:service name="ClientManagerService">
 
      <sca:interface.java interface="org.eclipse.stp.sca.example.ClientManagerService"/>
 
    </sca:service>
 
  </sca:component>
 
 
 
  <sca:component name="PurchaseProcessorComponent">
 
    <sca:implementation.java class="org.eclipse.stp.sca.example.PurchaseProcessorServiceImpl"/>
 
    <sca:service name="PurchaseProcessorService">
 
      <sca:interface.java interface="org.eclipse.stp.sca.example.PurchaseProcessorService"/>
 
    </sca:service>
 
  </sca:component>
 
 
 
  <sca:wire source="HandlePurchaseComponent/clientManager" target="ClientManagerComponent/ClientManagerService"/>
 
  <sca:wire source="HandlePurchaseComponent/purchaseProcessor" target="PurchaseProcessorComponent/PurchaseProcessorService"/>
 
 
 
  <sca:service name="HandlePurchaseService" promote="HandlePurchaseComponent/HandlePurchaseService"/>
 
</sca:composite>
 
</source>
 
 
 
What we are going to do is write and run unit tests for the first component '''HandlePurchaseComponent''' in the same way SCA testing tools would allow us to do.
 
  
 +
For commodity reasons, we copy the different interfaces in relation with this component (services and references).
 
The interface of the component services are the following:
 
The interface of the component services are the following:
  
 
<source lang="java">
 
<source lang="java">
package org.eclipse.stp.sca.example;
 
 
 
/**
 
/**
  * Service interface for a HandlePurchaseComponent service.
+
  * Interface for the RestaurantService.
 
  */
 
  */
public interface HandlePurchaseService {
+
public interface RestaurantService {
 +
/**
 +
* Get the menus.
 +
* @return an array of Menus.
 +
*/
 +
Menu[] getMenus();
  
 
/**
 
/**
* Handle purchase of items.
+
* Get the bill for a given menu.
*
+
* @param menu the menu we want the price.
* @param clientName the client name.
+
* @return the price of this menu, with the tip and the vat included.
* @param purchasedItemReferences an array of item references. Never null.
+
* @return a status indicating the result of the purchase processing.  
+
 
*/
 
*/
public PurchaseProcessingStatus handlePurchase(  
+
double getBill( Menu menu );
String clientName, String[] purchasedItemReferences );
+
 
}
 
}
 
</source>
 
</source>
Line 84: Line 52:
  
 
<source lang="java">
 
<source lang="java">
package org.eclipse.stp.sca.example;
 
 
 
/**
 
/**
  * Service interface for a PurchaseProcessorComponent service.
+
  * Interface for the MenuService.
 
  */
 
  */
public interface PurchaseProcessorService {
+
public interface MenuService {
 +
 +
/**
 +
* Get the menus.
 +
* @return an array of Menus.
 +
*/
 +
Menu[] getMenu();
  
 
/**
 
/**
* Process purchase.
+
* Get the price of a given menu.
* @param purchasedItemReferences an array of item references. Never null.
+
* @param menu the menu we want the price.
* @return true if the processing succeeded, false if an error occurred.  
+
* @return the price of this menu, without the tip and the vat.
 
*/
 
*/
public boolean processPurchase( String[] purchasedItemReferences );
+
double getPrice( Menu menu );
 
}
 
}
 
</source>
 
</source>
Line 102: Line 74:
  
 
<source lang="java">
 
<source lang="java">
package org.eclipse.stp.sca.example;
 
 
 
/**
 
/**
  * Service interface for a ClientManagerComponent service.
+
  * Interface for the BillService.
 
  */
 
  */
public interface ClientManagerService {
+
public interface BillService {
  
 
/**
 
/**
* Check whether a client is already registered or not.
+
* Get the bill from a menu price.
* @param clientName the client name.
+
* @param menuPrice the menu price, without the tip and the vat.
* @return true if the client is already known, false otherwise.
+
* @return the menu price with the tip and vat added.
 
*/
 
*/
public boolean isRegistered( String clientName );
+
double getBill( double menuPrice );
+
/**
+
* Keep track of a client in the history.
+
* Should not be called if <i>clientName</i> is already registered.
+
*
+
* @param clientName the client name.
+
* @return true if the registration succeeded, false otherwise.
+
*/
+
public boolean registerClient(  String clientName );
+
 
}
 
}
 
</source>
 
</source>
  
  
The implementation class of '''HandlePurchaseComponent''' is the following:
+
And the implementation class of '''RestaurantServiceComponent''' is the following:
  
 
<source lang="java">
 
<source lang="java">
package org.eclipse.stp.sca.example;
 
 
import org.osoa.sca.annotations.Service;
 
import org.osoa.sca.annotations.Reference;
 
 
 
/**
 
/**
  * Implementation for HandlePurchaseComponent.
+
  * Implementation for RestaurantServiceComponent.
 
  */
 
  */
@Service( HandlePurchaseService.class )
+
@Service( RestaurantService.class )
public class HandlePurchaseServiceImpl implements HandlePurchaseService {
+
public class RestaurantServiceImpl implements RestaurantService {
private ClientManagerService clientManager;
+
 
private PurchaseProcessorService purchaseProcessor;
+
private MenuService menuService;
+
private BillService billService;
 
 
 
@Reference
 
@Reference
public void setClientManager(ClientManagerService clientManager) {
+
public void setMenuService( MenuService menuService ) {
this.clientManager = clientManager;
+
this.menuService = menuService;
 
}
 
}
  
 
@Reference
 
@Reference
public void setPurchaseProcessor(PurchaseProcessorService purchaseProcessor) {
+
public void setBillService( BillService billService ) {
this.purchaseProcessor = purchaseProcessor;
+
this.billService = billService;
 
}
 
}
+
 
/*
+
public double getBill( Menu menu ) {
* (non-Javadoc)
+
double menuPrice = this.menuService.getPrice( menu );
* @see org.eclipse.stp.sca.example.HandlePurchaseService#handlePurchase(java.lang.String, java.lang.String[])
+
return this.billService.getBill( menuPrice );
*/
+
}
public PurchaseProcessingStatus handlePurchase(  
+
 
String clientName, String[] purchasedItemReferences ) {
+
public Menu[] getMenus() {
+
return this.menuService.getMenu();
// Registration.
+
boolean isRegistered = true;
+
if( !clientManager.isRegistered( clientName ))
+
isRegistered = clientManager.registerClient( clientName );
+
if( !isRegistered )
+
return PurchaseProcessingStatus.REGISTRATION_FAILED;
+
+
// Purchase processing.
+
boolean processingDone = purchaseProcessor.processPurchase( purchasedItemReferences );
+
if( !processingDone )
+
return PurchaseProcessingStatus.PROCESSING_FAILED;
+
+
return PurchaseProcessingStatus.OK;
+
 
}
 
}
 
}
 
}
 
</source>
 
</source>
  
 
+
The goal of this example is to show how we can perform unit test directly from the composite elements.
Notice that I have used Tuscany to run these examples. I also used Tuscany jars for the Java annotations.
+
A STP testing tool would use the annotations defined in the STP project.
+
This choice does not prevent us from seeing the portability of the implementation and of the testing mechanism.
+
 
+
You can also notice that the application is not very smart.
+
The goal of this example is not to show a smart SCA application, but how we can perform unit test directly with the composite elements.
+
  
 
<br />
 
<br />
Line 195: Line 132:
 
We first make a right-click on the SCA project we want to test and we select '''SCA &gt; Create Test Project'''.
 
We first make a right-click on the SCA project we want to test and we select '''SCA &gt; Create Test Project'''.
  
A project whose name is <projectName>Test is created if it does not already exists (<projectName> is the name of the project to test.
+
A project whose name is <projectName>Test is created (<projectName> is the name of the project to test).
This project is an SCA Java project, associated through one of its properties with the SCA project to test.
+
For commodity reasons, "the project to test" is called "tested project" and the project containing the test code is called "tester project".
For commodity reasons, "the project to test" will be called "tested project" and the project containing the test code will be called the "tester project".
+
This generated tester project is an SCA Java project, associated through one of its properties with the SCA project to test. In this example, since the tested project is also a Java project, they are related through the build path of the tester project.
The tester project has EasyMock and a custom library adapeted for SCA test in its class path. JUnit and SCA libraries (SCA Java annotations) are already present in Eclipse.
+
The tester project has EasyMock, JUnit, SCA libraries (for SCA Java annotations) and a custom library adapted for SCA test in its class path. It will contain all the tests we write for the business application.
  
  
Now this is done, we would like to wrtie unit tests for the first component '''HandlePurchaseComponent'''.
+
Now this is done, we would like to write unit tests for the first component '''RestaurantServiceComponent'''.
We maker a right-click on the component to test and we select '''SCA &gt; Create Unit Test'''.
+
We maker a right-click on the component (/ composite) to test and we select '''SCA &gt; Create Unit Test'''.
 
This right-click can be made in the SCA Composite Designer or in the XML editor for *.composite files.
 
This right-click can be made in the SCA Composite Designer or in the XML editor for *.composite files.
It results in the generation of the files in the tester project. Let's take a look at the interesting ones for instance.
+
It results in the generation of files in the tester project. Let's take a look at the interesting ones for instance.
  
 
The first thing is a composite file, which defines a composite with two components and one service.
 
The first thing is a composite file, which defines a composite with two components and one service.
The first component is a "JUnit component", whose implementation calls several a TestCase.  
+
The first component is a "test component", whose implementation includes a JUnit TestCase.  
The second component is the component to test, '''HandlePurchaseComponent''' (an exact copy-paste of the component, with its implementation and its interface imported into the project). Here is the composite diagram...
+
The second component is the component to test, '''RestaurantServiceComponent''' (an exact copy-paste of the component, with its implementation and its interface imported or referenced by the project). Here is the composite diagram...
  
[[Image:SCAtestSample_applicationTestComposite.png]]
+
[[Image:SCAtestSample_applicationTestComposite.gif]]
  
  
Line 217: Line 154:
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0" encoding="ISO-8859-15"?>
 
<?xml version="1.0" encoding="ISO-8859-15"?>
<sca:composite xmlns:sca="http://www.osoa.org/xmlns/sca/1.0" xmlns="http://www.osoa.org/xmlns/sca/1.0" name="TestHandlePurchase">
+
<sca:composite  
 
+
xmlns:sca="http://www.osoa.org/xmlns/sca/1.0"  
 +
xmlns="http://www.osoa.org/xmlns/sca/1.0"  
 +
name="TestRestaurantServiceComponent">
 +
 +
  <!-- Test Component -->
 
   <sca:component name="TestComponent">
 
   <sca:component name="TestComponent">
     <sca:implementation.java class="org.eclipse.stp.sca.example.test.run.ScaTestRunner"/>
+
     <sca:implementation.java class="org.eclipse.stp.sca.test.sample.impl.RestaurantServiceComponentRTSImpl"/>
     <sca:reference name="handlePurchaseService">
+
 
       <sca:binding.sca/>
+
  <sca:service name="RunTestService">
 +
      <tuscany:binding.rmi xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0"
 +
      host="localhost" port="8098" serviceName="TestSca"/>
 +
     </sca:service>
 +
   
 +
<sca:service name="BillService"><sca:binding.sca/></sca:service>
 +
    <sca:service name="MenuService"><sca:binding.sca/></sca:service>
 +
 
 +
  <sca:reference name="restaurantService">
 +
       <tuscany:binding.rmi xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0"
 +
      host="localhost" port="8099" serviceName="RestaurantServiceRMI" />
 
     </sca:reference>
 
     </sca:reference>
    <sca:service name="RunTestService">
 
      <tuscany:binding.rmi xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0" host="localhost" port="8099" serviceName="TestSca"/>
 
    </sca:service>
 
    <sca:service name="ClientManagerService">
 
      <sca:binding.sca/>
 
    </sca:service>
 
    <sca:service name="PurchaseProcessorService">
 
      <sca:binding.sca/>
 
    </sca:service>
 
 
   </sca:component>
 
   </sca:component>
 
    
 
    
   <sca:component name="HandlePurchaseComponent">
+
  <!-- Tested Component -->
     <sca:implementation.java class="org.eclipse.stp.sca.example.HandlePurchaseServiceImpl"/>
+
   <sca:component name="RestaurantServiceComponent">
     <sca:service name="HandlePurchaseService">
+
     <sca:implementation.java class="restaurant_rmi_service.lib.RestaurantServiceImpl"/>
       <sca:binding.sca/>
+
      
 +
<sca:service name="RestaurantService">
 +
       <sca:interface.java interface="restaurant_rmi_service.api.RestaurantService"/>
 +
      <tuscany:binding.rmi xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0"
 +
      host="localhost" port="8099" serviceName="RestaurantServiceRMI" />
 
     </sca:service>
 
     </sca:service>
     <sca:reference name="clientManager">
+
      
      <sca:binding.sca/>
+
<sca:reference name="billService"><sca:binding.sca/></sca:reference>
    </sca:reference>
+
     <sca:reference name="menuService"><sca:binding.sca/></sca:reference>
     <sca:reference name="purchaseProcessor">
+
      <sca:binding.sca/>
+
    </sca:reference>
+
 
   </sca:component>
 
   </sca:component>
 
    
 
    
   <sca:wire source="TestComponent/handlePurchaseService" target="HandlePurchaseComponent/HandlePurchaseService"/>
+
   <sca:wire source="TestComponent/restaurantService" target="RestaurantServiceComponent/RestaurantService"/>
   <sca:wire source="HandlePurchaseComponent/clientManager" target="TestComponent/ClientManagerService"/>
+
   <sca:wire source="RestaurantServiceComponent/billService" target="TestComponent/BillService"/>
   <sca:wire source="HandlePurchaseComponent/purchaseProcessor" target="TestComponent/PurchaseProcessorService"/>
+
   <sca:wire source="RestaurantServiceComponent/menuService" target="TestComponent/MenuService"/>
 
+
 
   <sca:service name="RunTestService" promote="TestComponent/RunTestService"/>
 
   <sca:service name="RunTestService" promote="TestComponent/RunTestService"/>
 
</sca:composite>
 
</sca:composite>
 +
 
</source>
 
</source>
  
  
The elements of the second components are already known. They come from the tested component.
+
The elements of the second component are already known. They come from the tested component.
You can notice that its references are wired to services of the JUnit component. These services' implementations will delegate the calls to mock up objects.
+
You can notice that its references are wired to services of the test component. These services' implementations will delegate the calls to mock objects.
The JUnit component has one service which is promoted bny the tester composite. Its interface is the following one:
+
  
<source lang="java">
+
The test component has one service which is promoted by the tester composite. Its interface is the following one:
package org.eclipse.stp.sca.example.test.generated;
+
  
 +
<source lang="java">
 
/**
 
/**
  * Interface for the JUnit component.
+
  * The interface to call SCA testing applications.  
 
  */
 
  */
 
public interface RunTestService {
 
public interface RunTestService {
Line 281: Line 223:
  
 
The implementation of this component is made up of two important classes.
 
The implementation of this component is made up of two important classes.
The implementationitself implements the service given above and all the references of the tested component.
+
The implementation itself implements the service given above and all the references of the tested component.
In this example, it means we implement RunTestService, ClientManagerService and PurchaseProcessorService. Here is the code.
+
In this example, it means we implement RunTestService, BillService and MenuService.  
  
 
<source lang="java">
 
<source lang="java">
package org.eclipse.stp.sca.example.test.run;
 
 
import java.lang.reflect.Method;
 
 
import junit.framework.TestResult;
 
import junit.framework.TestSuite;
 
 
import org.eclipse.stp.sca.example.ClientManagerService;
 
import org.eclipse.stp.sca.example.HandlePurchaseService;
 
import org.eclipse.stp.sca.example.PurchaseProcessorService;
 
import org.eclipse.stp.sca.example.test.generated.RunTestService;
 
import org.eclipse.stp.sca.example.test.generated.ScaMock;
 
import org.eclipse.stp.sca.example.test.generated.SerializableTestResult;
 
import org.eclipse.stp.sca.example.test.unit.HandlePurchaseComponentTestCase;
 
import org.osoa.sca.annotations.Reference;
 
import org.osoa.sca.annotations.Service;
 
 
 
/**
 
/**
  * Implementation for the JUnit component.
+
  * The implementation of the "test component".
 
  */
 
  */
@Service(interfaces={ RunTestService.class, ClientManagerService.class, PurchaseProcessorService.class })
+
@Service(interfaces={ RunTestService.class, BillService.class, MenuService.class })
public class ScaTestRunner implements RunTestService, ClientManagerService, PurchaseProcessorService {
+
public class RestaurantServiceComponentRTSImpl implements RunTestService, BillService, MenuService {
private HandlePurchaseService handlePurchaseService;
+
/** The referenced RestaurantService. */
 +
private RestaurantService restaurantService;
  
 +
 
/**
 
/**
* Run a HandlePurchaseComponentTestCase instance.
+
* Run a RestaurantServiceComponentTC instance.
* @see org.eclipse.stp.sca.example.test.generated.RunTestService#runScaTest()
+
* @see org.eclipse.stp.sca.test.sample.library.RunTestService#runScaTest()
 
*/
 
*/
 
public SerializableTestResult runScaTest() {
 
public SerializableTestResult runScaTest() {
 
 
 
TestSuite testSuite = new TestSuite( "Sca Test Suite"  );
 
TestSuite testSuite = new TestSuite( "Sca Test Suite"  );
for( Method m : HandlePurchaseComponentTestCase.class.getMethods()) {
+
for( Method m : RestaurantServiceComponentTC.class.getMethods()) {
 
if( !m.getName().startsWith( "test" ))
 
if( !m.getName().startsWith( "test" ))
 
continue;
 
continue;
 
 
testSuite.addTest( new HandlePurchaseComponentTestCase( m.getName(), handlePurchaseService ));
+
testSuite.addTest( new RestaurantServiceComponentTC( m.getName(), restaurantService ));
 
}
 
}
 
 
Line 331: Line 258:
 
 
 
/**
 
/**
* @param handlePurchaseService the handlePurchaseService to set
+
* @param restaurantService the restaurantService to set
 
*/
 
*/
 
@Reference
 
@Reference
public void setHandlePurchaseService(HandlePurchaseService handlePurchaseService) {
+
public void setRestaurantService( RestaurantService restaurantService ) {
this.handlePurchaseService = handlePurchaseService;
+
this.restaurantService = restaurantService;
 
}
 
}
 
 
 
/**
 
/**
 
* Delegate the call to the mock.
 
* Delegate the call to the mock.
* @see ClientManagerServiceMock#isRegistered(String)
+
* @see BillService#getBill(double)
 
*/
 
*/
public boolean isRegistered(String clientName) {
+
public double getBill( double menuPrice ) {
ClientManagerService mock =  
+
BillService mock =  
(ClientManagerService) ScaMock.getInstance().get( ClientManagerService.class );
+
(BillService) ScaMock.getInstance().get( BillService.class );
return mock.isRegistered( clientName );
+
return mock.getBill( menuPrice );
 
}
 
}
  
 
/**
 
/**
 
* Delegate the call to the mock.
 
* Delegate the call to the mock.
* @see ClientManagerServiceMock#registerClient(String)
+
* @see MenuService#getMenu()
 
*/
 
*/
public boolean registerClient(String clientName) {
+
public Menu[] getMenu() {
ClientManagerService mock =  
+
MenuService mock =  
(ClientManagerService) ScaMock.getInstance().get( ClientManagerService.class );
+
(MenuService) ScaMock.getInstance().get( MenuService.class );
return mock.registerClient( clientName );
+
return mock.getMenu();
 
}
 
}
+
 
 
/**
 
/**
 
* Delegate the call to the mock.
 
* Delegate the call to the mock.
* @see PurchaseProcessorService#processPurchase(String[])
+
* @see MenuService#getPrice(Menu)
 
*/
 
*/
public boolean processPurchase(String[] purchasedItemReferences) {
+
public double getPrice( Menu menu ) {
PurchaseProcessorService mock =  
+
MenuService mock =  
(PurchaseProcessorService) ScaMock.getInstance().get( PurchaseProcessorService.class );
+
(MenuService) ScaMock.getInstance().get( MenuService.class );
return mock.processPurchase( purchasedItemReferences );
+
return mock.getPrice( menu );
 
}
 
}
 
}
 
}
Line 371: Line 298:
  
  
You may have noticed that the service to test is present as a reference in this class. Let's now take a look at the methods of this class.
+
Notice that the service to test is present as a reference in this class. Let's now take a look at the methods of this class.
  
 
All the references' operations have the same behaviour. They get a mock object from a singleton class and delegate the call to this object.
 
All the references' operations have the same behaviour. They get a mock object from a singleton class and delegate the call to this object.
The main method, runScaTest, creates a TestSuite, instanciates a TestCase and adds all the methods which begins with "test". It then runs this TestSuite and returns the result as a SerializableTestResult.
 
  
Only the TestCase skeleton is generated, testing methods have to be written by the user.
+
The main method, runScaTest, creates a TestSuite, instanciates a TestCase and register all the methods which begins with "test". It then runs this TestSuite and returns the result as a SerializableTestResult.
 +
 
 +
 
 +
The last generated class is a TestCase (only its skeleton is generated, testing methods have to be written by the user).
  
 
<source lang="java">
 
<source lang="java">
 
// @ScaTest( composite="{targetNamespace}compositeName" name="componentA" )
 
// @ScaTest( composite="{targetNamespace}compositeName" name="componentA" )
 
// Annotation not yet defined. Attributes to review.
 
// Annotation not yet defined. Attributes to review.
// Useful only inside an Eclipse project, when using a builder.
+
// Useful only inside an Eclipse project, when using a builder to synchronize business and test code.
public class HandlePurchaseComponentTestCase extends TestCase {
+
public class RestaurantServiceComponentTC extends TestCase {
 
// Mocks.
 
// Mocks.
/** The mock for ClientManagerService. */
+
/** The mock for BillService. */
private ClientManagerService clientManagerMock =  
+
private BillService billService =  
(ClientManagerService) ScaMock.getInstance().get( ClientManagerService.class );
+
(BillService) ScaMock.getInstance().get( BillService.class );
 
 
/** The mock for PurchaseProcessorService. */
+
/** The mock for MenuService. */
private PurchaseProcessorService purchaseProcessorMock =  
+
private MenuService menuService =  
(PurchaseProcessorService) ScaMock.getInstance().get( PurchaseProcessorService.class );
+
(MenuService) ScaMock.getInstance().get( MenuService.class );
 +
 +
// Referenced services.
 +
/**  */
 +
private RestaurantService restaurantService;
 
 
// References.
 
private HandlePurchaseService handlePurchaseService;
 
 
 
 
/**
 
/**
* @param fName
+
* Constructor that stores an instance of the services to test.
* @param handlePurchaseService the handlePurchaseService to set
+
* In a generation approach, this constructor should have as parameters the name
 +
* of the test method to run (fName), and an instance of all the referenced services
 +
* of the element to test (component or composite).
 +
*
 +
* @param fName the name of the method to run in this TestCase.
 +
* @param restaurantService the restaurantService to set.
 
*/
 
*/
public HandlePurchaseComponentTestCase( String fName, HandlePurchaseService handlePurchaseService ) {
+
public RestaurantServiceComponentTC( String fName, RestaurantService restaurantService ) {
 
super( fName );
 
super( fName );
this.handlePurchaseService = handlePurchaseService;
+
this.restaurantService = restaurantService;
 
}
 
}
  
 +
/**
 +
* Reset the behavior of the mock objects during each test.
 +
*/
 
@Override
 
@Override
protected void tearDown() throws Exception {
+
protected void setUp() throws Exception {
reset( clientManagerMock );
+
reset( billService );
reset( purchaseProcessorMock );
+
reset( menuService );
 
}
 
}
 
 
/* TODO: write testing methods after this comment. */
+
/* TODO: write testing methods after this comment.
 +
* Testing method names should begin with "test".
 +
*/
 
}
 
}
 
</source>
 
</source>
  
  
This skeleton has two attributes, which are the mocked up references.
+
This skeleton has two attributes which are the mocked references.
 
It has a third attribute which the service of the component to test (set in the constructor).
 
It has a third attribute which the service of the component to test (set in the constructor).
Eventually, we configure the tearDown method to reset the behavior of the mocks between each run.
+
Eventually, we configure the setUp method to reset the behavior of the mocks between each run.
  
Until now, all of this has been generated.
 
The only thing the user has to do is writing testing methods.
 
  
<source lang="java">
+
'''Until now, all of this can be generated by a tool. In this case, the only remaining thing for the user is writing test methods.'''
package org.eclipse.stp.sca.example.test.unit;
+
 
+
import static org.easymock.EasyMock.expect;
+
import static org.easymock.EasyMock.replay;
+
import static org.easymock.EasyMock.reset;
+
import static org.easymock.EasyMock.aryEq;
+
import junit.framework.TestCase;
+
  
import org.eclipse.stp.sca.example.ClientManagerService;
+
Do not expect the following tests to be smart. They were written to show what could be (and not what should be) done to test such an application.
import org.eclipse.stp.sca.example.HandlePurchaseService;
+
import org.eclipse.stp.sca.example.PurchaseProcessingStatus;
+
import org.eclipse.stp.sca.example.PurchaseProcessorService;
+
import org.eclipse.stp.sca.example.test.generated.ScaMock;
+
  
 +
<source lang="java">
 
// @ScaTest( composite="{targetNamespace}compositeName" name="componentA" )
 
// @ScaTest( composite="{targetNamespace}compositeName" name="componentA" )
 
// Annotation not yet defined. Attributes to review.
 
// Annotation not yet defined. Attributes to review.
// Useful only inside an Eclipse project, when using a builder.
+
// Useful only inside an Eclipse project, when using a builder to synchronize business and test code.
public class HandlePurchaseComponentTestCase extends TestCase {
+
public class RestaurantServiceComponentTC extends TestCase {
 
// Mocks.
 
// Mocks.
/** The mock for ClientManagerService. */
+
/** The mock for BillService. */
private ClientManagerService clientManagerMock =  
+
private BillService billService =  
(ClientManagerService) ScaMock.getInstance().get( ClientManagerService.class );
+
(BillService) ScaMock.getInstance().get( BillService.class );
 +
 +
/** The mock for MenuService. */
 +
private MenuService menuService =
 +
(MenuService) ScaMock.getInstance().get( MenuService.class );
 
 
/** The mock for PurchaseProcessorService. */
+
// Referenced services.
private PurchaseProcessorService purchaseProcessorMock =
+
/** */
(PurchaseProcessorService) ScaMock.getInstance().get( PurchaseProcessorService.class );
+
private RestaurantService restaurantService;
 
 
// References.
 
private HandlePurchaseService handlePurchaseService;
 
 
 
 
/**
 
/**
* @param fName
+
* Constructor that stores an instance of the services to test.
* @param handlePurchaseService the handlePurchaseService to set
+
* In a generation approach, this constructor should have as parameters the name
 +
* of the test method to run (fName), and an instance of all the referenced services
 +
* of the element to test (component or composite).
 +
*
 +
* @param fName the name of the method to run in this TestCase.
 +
* @param restaurantService the restaurantService to set.
 
*/
 
*/
public HandlePurchaseComponentTestCase( String fName, HandlePurchaseService handlePurchaseService ) {
+
public RestaurantServiceComponentTC( String fName, RestaurantService restaurantService ) {
 
super( fName );
 
super( fName );
this.handlePurchaseService = handlePurchaseService;
+
this.restaurantService = restaurantService;
 
}
 
}
  
 +
/**
 +
* Reset the behavior of the mock objects during each test.
 +
*/
 
@Override
 
@Override
protected void tearDown() throws Exception {
+
protected void setUp() throws Exception {
reset( clientManagerMock );
+
reset( billService );
reset( purchaseProcessorMock );
+
reset( menuService );
 
}
 
}
 
 
/* TODO: write testing methods after this comment. */
+
/* TODO: write testing methods after this comment.
 +
* Testing method names should begin with "test".
 +
*/
 
 
 
/**
 
/**
 
* Test 1.
 
* Test 1.
* Client registration worked but processing fails.
+
* Check the getMenus() operation of the service.
* Check the result.
+
 
*/
 
*/
 
public void test1() {
 
public void test1() {
String clientName = "James Sawyer";
+
Menu[] expectedMenus = new Menu[] {
String[] items = new String[] { "item1", "item5" };
+
new MenuImpl( 0, "Hamburger with french fries" ),
 +
new MenuImpl( 1, "Salad with eggs and olives" )
 +
};
 
 
// Mock 1: expect a call to isRegistered with clientName as argument, and return true.
+
// We are going to call getMenus() on the service.
expect( clientManagerMock.isRegistered( clientName )).andReturn( true );
+
// We know its implementation makes one call to menuService.getMenu().
replay( clientManagerMock );
+
// Mock menuService: expect a call to getMenus, and return a list of menus.
 +
expect( menuService.getMenu()).andReturn( expectedMenus );
 +
replay( menuService );
 +
 
 +
// Call getMenus() and check the result.
 +
Menu[] menus = restaurantService.getMenus();
 +
assertEquals( 2, menus.length );
 
 
// Any other call to this reference should throw an exception, not managed here.
+
// Is there a hamburger in the menu ?
+
boolean thereIsHamburger = false;
// Mock 2: expect a call to processPurchase with items as argument, and return false.
+
for( Menu menu : menus )
expect( purchaseProcessorMock.processPurchase( aryEq( items ))).andReturn( false );
+
thereIsHamburger = thereIsHamburger || menu.printMenu().toLowerCase().contains( "hamburger" );
replay( purchaseProcessorMock );
+
assertTrue( thereIsHamburger );
+
// Any other call to this reference should throw an exception, not managed here.
+
+
// Call the service and check the result.
+
PurchaseProcessingStatus result = handlePurchaseService.handlePurchase( clientName, items );
+
assertEquals( PurchaseProcessingStatus.PROCESSING_FAILED, result );
+
 
}
 
}
 
 
 
/**
 
/**
 
* Test 2.
 
* Test 2.
* Client registration and processing works.
+
* Check the getBill(Menu) operation of the service.
* Check the result.
+
* We here forget to define the behavior of billService. This test should fail.
 +
* Notice that it will not throw an EasyMock exception since we do not replay the billService mock.
 
*/
 
*/
 
public void test2() {
 
public void test2() {
String clientName = "James Sawyer";
+
Menu expectedMenu = new MenuImpl( 0, "Hamburger with french fries" );
final String[] items = new String[] { "item1", "item5" };
+
+
// Mock 1: expect a call to isRegistered with clientName as argument, and return true.
+
expect( clientManagerMock.isRegistered( clientName )).andReturn( true );
+
replay( clientManagerMock );
+
 
 
// Any other call to this reference should throw an exception, not managed here.
+
// We are going to call getBill(Menu) on the service.
+
// We know its implementation makes one call to menuService.getPrice(Menu).
// Mock 2: expect a call to processPurchase with items as argument, and return true.
+
// Mock menuService: expect a call to getPrice(Menu), and return 20.0
expect( purchaseProcessorMock.processPurchase( aryEq( items ))).andReturn( true );
+
expect( menuService.getPrice( expectedMenu )).andReturn( 20.0 );
replay( purchaseProcessorMock );
+
replay( menuService );
 
 
 
// Any other call to this reference should throw an exception, not managed here.
 
// Any other call to this reference should throw an exception, not managed here.
 
 
// Call the service and check the result.
+
// Call getBill(Menu) and check the result.
PurchaseProcessingStatus result = handlePurchaseService.handlePurchase( clientName, items );
+
double bill = restaurantService.getBill( expectedMenu );
assertEquals( PurchaseProcessingStatus.OK, result );
+
assertEquals( 25.0, bill );
 
}
 
}
 
 
 
/**
 
/**
 
* Test 3.
 
* Test 3.
* Client not registered.
+
* Check the getBill(Menu) operation of the service.
* Check the result.  
+
* Same test than test2, with another exception. This test should be an error.
 +
* This time, we replay the billService mock but we still do not foresee the use of a operation.
 
*  
 
*  
* Should throw an exception because the implementation should register the client
+
* It shows that the user has to replay the mock references in every method and for every mock.
* and this is not foreseen in the mock.  
+
* Just to test TestCase failures.
+
 
*/
 
*/
 
public void test3() {
 
public void test3() {
String clientName = "James Sawyer";
+
Menu expectedMenu = new MenuImpl( 0, "Hamburger with french fries" );
String[] items = new String[] { "item1", "item5" };
+
+
// We are going to call getBill(Menu) on the service.
// Mock 1: expect a call to isRegistered with clientName as argument, and return true.
+
// We know its implementation makes one call to menuService.getPrice(Menu).
expect( clientManagerMock.isRegistered( clientName )).andReturn( false );
+
// Mock menuService: expect a call to getPrice(Menu), and return 20.0
replay( clientManagerMock );
+
expect( menuService.getPrice( expectedMenu )).andReturn( 20.0 );
 +
replay( menuService );
 
 
 
// Any other call to this reference should throw an exception, not managed here.
 
// Any other call to this reference should throw an exception, not managed here.
 
 
// Mock 2: expect a call to processPurchase with items as argument, and return true.
+
// We also know its implementation makes one call to billService.getBill(double).
expect( purchaseProcessorMock.processPurchase( aryEq( items ))).andReturn( true );
+
replay( billService );
replay( purchaseProcessorMock );
+
 
 
 
// Any other call to this reference should throw an exception, not managed here.
 
// Any other call to this reference should throw an exception, not managed here.
 
 
// Call the service and check the result.
+
// Call getBill(Menu) and check the result.
PurchaseProcessingStatus result = handlePurchaseService.handlePurchase( clientName, items );
+
double bill = restaurantService.getBill( expectedMenu );
assertEquals( PurchaseProcessingStatus.OK, result );
+
assertEquals( 25.0, bill );
 
}
 
}
 
 
 
/**
 
/**
 
* Test 4.
 
* Test 4.
* Client not registered.
+
* Check the getBill(Menu) operation of the service.
* Check the result.  
+
* Same test than test2 but we here define all the behaviors.
*  
+
* The same than test3 but this time, we foresee the call to registerClient in the mock.  
+
 
*/
 
*/
 
public void test4() {
 
public void test4() {
String clientName = "James Sawyer";
+
Menu expectedMenu = new MenuImpl( 0, "Hamburger with french fries" );
String[] items = new String[] { "item1", "item5" };
+
+
// We are going to call getBill(Menu) on the service.
// Mock 1: expect a call to isRegistered with clientName as argument, and return true.
+
// We know its implementation makes one call to menuService.getPrice(Menu).
expect( clientManagerMock.isRegistered( clientName )).andReturn( false );
+
// Mock menuService: expect a call to getPrice(Menu), and return 20.0
expect( clientManagerMock.registerClient( clientName )).andReturn( true );
+
expect( menuService.getPrice( expectedMenu )).andReturn( 20.0 );
replay( clientManagerMock );
+
replay( menuService );
 
 
 
// Any other call to this reference should throw an exception, not managed here.
 
// Any other call to this reference should throw an exception, not managed here.
 
 
// Mock 2: expect a call to processPurchase with items as argument, and return true.
+
// We also know its implementation makes one call to billService.getBill(double).
expect( purchaseProcessorMock.processPurchase( aryEq( items ))).andReturn( true );
+
// Mock billService: expect a call to getBill(double), and return 25.0
replay( purchaseProcessorMock );
+
expect( billService.getBill( 20.0 )).andReturn( 25.0 );
 +
replay( billService );
 
 
 
// Any other call to this reference should throw an exception, not managed here.
 
// Any other call to this reference should throw an exception, not managed here.
 
 
// Call the service and check the result.
+
// Call getBill(Menu) and check the result.
PurchaseProcessingStatus result = handlePurchaseService.handlePurchase( clientName, items );
+
double bill = restaurantService.getBill( expectedMenu );
assertEquals( PurchaseProcessingStatus.OK, result );
+
assertEquals( 25.0, bill );
 +
}
 +
 +
/**
 +
* Test 5.
 +
* Check the getBill(Menu) operation of the service.
 +
* Same test than test4 but we are here less strict on mock calls.
 +
* This illustrates the use of EasyMock.
 +
*
 +
* For more details about available features of EasyMock, check their documentation at
 +
* {@linkplain http://www.easymock.org/EasyMock2_4_Documentation.html}.
 +
*/
 +
public void test5() {
 +
Menu expectedMenu = new MenuImpl( 0, "Hamburger with french fries" );
 +
 +
// Mock menuService: expect a call to getPrice(Menu), and return 20.0
 +
// Change: we can call getPrice an undetermined number of times.
 +
// Change: we can call getPrice with any Menu parameter.
 +
expect( menuService.getPrice( isA( Menu.class ))).andReturn( 20.0 ).anyTimes();
 +
replay( menuService );
 +
 +
// Any other call to this reference should throw an exception, not managed here.
 +
 +
// Mock billService: expect a call to getBill(double), and return 25.0
 +
// Change: return 25.0 each time 20.0 is given as argument.
 +
// And return -1.0 then for any argument different from 20.0.
 +
expect( billService.getBill( 20.0 )).andReturn( 25.0 ).anyTimes();
 +
expect( billService.getBill( not( eq( 20.0 )))).andStubReturn( -1.0 );
 +
replay( billService );
 +
 +
// Any other call to this reference should throw an exception, not managed here.
 +
 +
// Call getBill(Menu) and check the result.
 +
double bill = restaurantService.getBill( expectedMenu );
 +
assertEquals( 25.0, bill );
 
}
 
}
 
}
 
}
 
</source>
 
</source>
  
In these testing methods, we defined the behavior of the "references" (the mock objects) by using EasyMock methods, and we check assertions on
+
In these test methods, we defined the behavior of the "references" (the mock objects) by using EasyMock methods, and we check assertions on service operation results. The difficulty here is how you can foresee the behavior of the references. It requires you to know how the references are used in the implementation of the component. From this point of view, you do not test only the service service contract but also the way and how the service is implemented. We are not really in a Test Driven Approach.
service operation results. The difficulty here is how you can foresee the behavior of the references. It requires you to know how the references are used in the implementation of the component. From this point of view, you do not test only the service service contract but also the way and how the service is implemented.
+
 
 +
<br />
  
 
== Runnning the Tests ==  
 
== Runnning the Tests ==  
  
We know have written a TestCase for SCA.
+
We now have written a TestCase for SCA and we need to run it on a SCA platform. This example has been run manually on a Tuscany runtime (i.e. Tuscany jars were added manualy to the project class path). But there is no reason for this application to not work with Fabric3 or Frascati. In the future, SCA platforms will be registered as servers in Eclipse (like J2EE servers - registration through the SOAS sub-project of STP). For the moment, [http://tuscany.apache.org/ Tuscany], [http://fabric3.codehaus.org/ Fabric3] and [http://petals.objectweb.org/ PEtALS] (with its SCA service engine based on [http://www.scorware.org/projects/en Frascati]) are expected to be supported before the end of the year.
We know need to run it on a SCA platform. For this example, I have run it manually on a Tuscany runtime (i.e. I have added Tuscany jars to my project class path). In the future, SCA platforms will be registered as servers in Eclipse (like J2EE servers - registration through the SOAS sub-project of STP). For the moment, Tuscany, Fabric3 and PEtALS (with its SCA service engine based on Frascati) are expected to be supported.
+
  
Running test is made by right-clicking on the TestCase to run and by selecting SCA > Run > ''platform'', where ''platform '' is a registered runtime server.
+
Running test is made by right-clicking on the TestCase to run and by selecting '''SCA &gt; Run &gt; <i>platform</i>''', where ''platform'' is a registered runtime server. It results in the deployment of the tester application and in the generation of a client to call this application.
It results in the deployment of the tester application and in the generation of a client to call this application.
+
  
 
The server...
 
The server...
  
 
<source lang="java">
 
<source lang="java">
package org.eclipse.stp.sca.example.test.client;
 
 
import java.io.IOException;
 
 
import org.apache.tuscany.sca.host.embedded.SCADomain;
 
 
 
/**
 
/**
 
  * Deploy the SCA test composite on a Tuscany runtime.
 
  * Deploy the SCA test composite on a Tuscany runtime.
Line 610: Line 573:
 
public static void main( String[] args ) throws IOException {
 
public static void main( String[] args ) throws IOException {
 
System.out.println( "Starting Test Application" );
 
System.out.println( "Starting Test Application" );
SCADomain scaDomain = SCADomain.newInstance( "TestHandlePurchase.composite" );
+
SCADomain scaDomain = SCADomain.newInstance( "TestRestaurantServiceComponent.composite" );
 
 
 
System.out.println( "Press any key to Exit" );
 
System.out.println( "Press any key to Exit" );
Line 624: Line 587:
  
 
<source lang="java">
 
<source lang="java">
package org.eclipse.stp.sca.example.test.client;
 
 
import java.net.MalformedURLException;
 
import java.rmi.Naming;
 
import java.rmi.NotBoundException;
 
import java.rmi.RemoteException;
 
 
import org.eclipse.stp.sca.example.test.generated.RunTestService;
 
import org.eclipse.stp.sca.example.test.generated.SerializableTestResult;
 
 
 
/**
 
/**
 
  * A simple test client to call the SCA test application via RMI and print the result of the test.
 
  * A simple test client to call the SCA test application via RMI and print the result of the test.
Line 640: Line 593:
  
 
public static void main( String[] args ) throws MalformedURLException, RemoteException, NotBoundException {
 
public static void main( String[] args ) throws MalformedURLException, RemoteException, NotBoundException {
RunTestService scaTestRunner = (RunTestService) Naming.lookup( "//localhost:8099/TestSca" );
+
RunTestService scaTestRunner = (RunTestService) Naming.lookup( "//localhost:8098/TestSca" );
 
SerializableTestResult tc = scaTestRunner.runScaTest();
 
SerializableTestResult tc = scaTestRunner.runScaTest();
 
System.out.println( tc.toString());
 
System.out.println( tc.toString());
Line 647: Line 600:
 
</source>
 
</source>
  
In Eclipse, results should rather be displayed in the usual JUnit view instead of being printed in the console.
 
With the given code, when both are run, it prints:
 
<pre>
 
4 tests were run.
 
  
Failure(s): 0
+
When both of these classes are run, it prints:
 +
<pre><nowiki>
 +
====== Test Result ======
 +
=========================
 +
5 tests were run.
 +
 
 +
Failure(s): 1
 +
+ test2(org.eclipse.stp.sca.test.sample.impl.RestaurantServiceComponentTC): expected:<25.0> but was:<0.0>
  
 
Error(s): 1
 
Error(s): 1
+ test3(org.eclipse.stp.sca.example.test.unit.HandlePurchaseComponentTestCase):
+
+ test3(org.eclipse.stp.sca.test.sample.impl.RestaurantServiceComponentTC): null
  Unexpected method call registerClient("James Sawyer"):
+
  
 
==> Test failed.
 
==> Test failed.
</pre>
+
</nowiki></pre>
 +
 
 +
 
 +
In Eclipse, results should rather be displayed in the usual JUnit view instead of being printed in the console.
 +
The example of this application is available in the SVN of the SCA sub-project of STP (project org.eclipse.stp.sca.test.sample in the incubation directory).
 +
 
 +
 
 +
[[Category:SOA_Tools]]
 +
[[Category:SOA]]
 +
[[Category:STP]]
 +
[[Category:SCA]]

Latest revision as of 04:37, 9 November 2010

The Application to Test

The application we are using as basis is the "Restaurant - RMI Service" application available in the examples of SCA.

You can find the source of this application in the SVN or by selecting File > New > Other > Examples > SCA > SCA Examples. In this page, the project as been saved in the workspace as the RestaurantRMISample project. It contains the composite, the interfaces and the implementations.


SCAtestSample applicationBasisProject.png


Our application is made up of five components:

  • RestaurantServiceComponent is the first component. It has one service which is promoted by the composite. And it has two references toward BillServiceComponent and MenuServiceComponent.
  • BillServiceComponent is the second component. It has one service wired with RestaurantServiceComponent and has a reference toward VatServiceComponent.
  • MenuServiceComponent is the third component. It also has one service wired RestaurantServiceComponent and has a reference toward TipServiceComponent.
  • The last two components, VatServiceComponent and TipServiceComponent, have each one a service and no reference.


Using the SCA Composite Designer, we got this diagram.

SCAtestSample applicationBusinessComposite.gif


The composite source can be found in the SVN. What we are going to do is to write and run unit tests for the first component RestaurantServiceComponent in the same way SCA testing tools would allow us to do.


For commodity reasons, we copy the different interfaces in relation with this component (services and references). The interface of the component services are the following:

/**
 * Interface for the RestaurantService.
 */
public interface RestaurantService {
	/**
	 * Get the menus.
	 * @return an array of Menus.
	 */
	Menu[] getMenus();
 
	/**
	 * Get the bill for a given menu.
	 * @param menu the menu we want the price.
	 * @return the price of this menu, with the tip and the vat included.
	 */
	double getBill( Menu menu );
}


/**
 * Interface for the MenuService.
 */
public interface MenuService {
 
	/**
	 * Get the menus.
	 * @return an array of Menus.
	 */
	Menu[] getMenu();
 
	/**
	 * Get the price of a given menu.
	 * @param menu the menu we want the price.
	 * @return the price of this menu, without the tip and the vat.
	 */
	double getPrice( Menu menu );
}


/**
 * Interface for the BillService.
 */
public interface BillService {
 
	/**
	 * Get the bill from a menu price.
	 * @param menuPrice the menu price, without the tip and the vat.
	 * @return the menu price with the tip and vat added.
	 */
	double getBill( double menuPrice );	
}


And the implementation class of RestaurantServiceComponent is the following:

/**
 * Implementation for RestaurantServiceComponent.
 */
@Service( RestaurantService.class )
public class RestaurantServiceImpl implements RestaurantService {
 
	private MenuService menuService;
	private BillService billService;
 
	@Reference
	public void setMenuService( MenuService menuService ) {
		this.menuService = menuService;
	}
 
	@Reference
	public void setBillService( BillService billService ) {
		this.billService = billService;
	}
 
	public double getBill( Menu menu ) {
		double menuPrice = this.menuService.getPrice( menu );
		return this.billService.getBill( menuPrice );
	}
 
	public Menu[] getMenus() {
		return this.menuService.getMenu();
	}
}

The goal of this example is to show how we can perform unit test directly from the composite elements.


Generating Test Project and Classes

First thing, we want to separate the business code from the testing code. Since SCA projects can be big, it seems better to create a distinct project in a workspace to test this application. We first make a right-click on the SCA project we want to test and we select SCA > Create Test Project.

A project whose name is <projectName>Test is created (<projectName> is the name of the project to test). For commodity reasons, "the project to test" is called "tested project" and the project containing the test code is called "tester project". This generated tester project is an SCA Java project, associated through one of its properties with the SCA project to test. In this example, since the tested project is also a Java project, they are related through the build path of the tester project. The tester project has EasyMock, JUnit, SCA libraries (for SCA Java annotations) and a custom library adapted for SCA test in its class path. It will contain all the tests we write for the business application.


Now this is done, we would like to write unit tests for the first component RestaurantServiceComponent. We maker a right-click on the component (/ composite) to test and we select SCA > Create Unit Test. This right-click can be made in the SCA Composite Designer or in the XML editor for *.composite files. It results in the generation of files in the tester project. Let's take a look at the interesting ones for instance.

The first thing is a composite file, which defines a composite with two components and one service. The first component is a "test component", whose implementation includes a JUnit TestCase. The second component is the component to test, RestaurantServiceComponent (an exact copy-paste of the component, with its implementation and its interface imported or referenced by the project). Here is the composite diagram...

SCAtestSample applicationTestComposite.gif


... and its source.

<?xml version="1.0" encoding="ISO-8859-15"?>
<sca:composite 
	xmlns:sca="http://www.osoa.org/xmlns/sca/1.0" 
	xmlns="http://www.osoa.org/xmlns/sca/1.0" 
	name="TestRestaurantServiceComponent">
 
  <!-- Test Component --> 
  <sca:component name="TestComponent">
    <sca:implementation.java class="org.eclipse.stp.sca.test.sample.impl.RestaurantServiceComponentRTSImpl"/>
 
   <sca:service name="RunTestService">
      <tuscany:binding.rmi xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0" 
      	host="localhost" port="8098" serviceName="TestSca"/>
    </sca:service>
 
	<sca:service name="BillService"><sca:binding.sca/></sca:service>
    <sca:service name="MenuService"><sca:binding.sca/></sca:service>
 
   <sca:reference name="restaurantService">
      <tuscany:binding.rmi xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0" 
      	host="localhost" port="8099" serviceName="RestaurantServiceRMI" />
    </sca:reference>
  </sca:component>
 
  <!-- Tested Component --> 
  <sca:component name="RestaurantServiceComponent">
    <sca:implementation.java class="restaurant_rmi_service.lib.RestaurantServiceImpl"/>
 
	<sca:service name="RestaurantService">
      <sca:interface.java interface="restaurant_rmi_service.api.RestaurantService"/>
      <tuscany:binding.rmi xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.0" 
      	host="localhost" port="8099" serviceName="RestaurantServiceRMI" />
    </sca:service>
 
	<sca:reference name="billService"><sca:binding.sca/></sca:reference>
    <sca:reference name="menuService"><sca:binding.sca/></sca:reference>
  </sca:component>
 
  <sca:wire source="TestComponent/restaurantService" target="RestaurantServiceComponent/RestaurantService"/>
  <sca:wire source="RestaurantServiceComponent/billService" target="TestComponent/BillService"/>
  <sca:wire source="RestaurantServiceComponent/menuService" target="TestComponent/MenuService"/>
  <sca:service name="RunTestService" promote="TestComponent/RunTestService"/>
</sca:composite>


The elements of the second component are already known. They come from the tested component. You can notice that its references are wired to services of the test component. These services' implementations will delegate the calls to mock objects.

The test component has one service which is promoted by the tester composite. Its interface is the following one:

/**
 * The interface to call SCA testing applications. 
 */
public interface RunTestService {
 
	/**
	 * Run a TestCase and return the result.
	 * @return the result of a TestCase as a serializable object.
	 */
	public SerializableTestResult runScaTest();
}

SerializableTestResult is a class that embeds the fields of the TestResult (the result of a JUnit test) and which is serializable, so that we can pass it through RMI. It is contained in the SCA Test library (not described here).

The implementation of this component is made up of two important classes. The implementation itself implements the service given above and all the references of the tested component. In this example, it means we implement RunTestService, BillService and MenuService.

/**
 * The implementation of the "test component".
 */
@Service(interfaces={ RunTestService.class, BillService.class, MenuService.class })
public class RestaurantServiceComponentRTSImpl implements RunTestService, BillService, MenuService {
	/** The referenced RestaurantService. */
	private RestaurantService restaurantService;
 
 
	/**
	 * Run a RestaurantServiceComponentTC instance.
	 * @see org.eclipse.stp.sca.test.sample.library.RunTestService#runScaTest()
	 */
	public SerializableTestResult runScaTest() {		
 
		TestSuite testSuite = new TestSuite( "Sca Test Suite"  );
		for( Method m : RestaurantServiceComponentTC.class.getMethods()) {
			if( !m.getName().startsWith( "test" ))
				continue;
 
			testSuite.addTest( new RestaurantServiceComponentTC( m.getName(), restaurantService ));
		}
 
		TestResult tcResult = new TestResult();
		testSuite.run( tcResult );
 
		SerializableTestResult serialTc = new SerializableTestResult( tcResult );
		return serialTc;
	}
 
	/**
	 * @param restaurantService the restaurantService to set
	 */
	@Reference
	public void setRestaurantService( RestaurantService restaurantService ) {
		this.restaurantService = restaurantService;
	}
 
	/**
	 * Delegate the call to the mock.
	 * @see BillService#getBill(double)
	 */
	public double getBill( double menuPrice ) {
		BillService mock = 
			(BillService) ScaMock.getInstance().get( BillService.class );
		return mock.getBill( menuPrice );
	}
 
	/**
	 * Delegate the call to the mock.
	 * @see MenuService#getMenu()
	 */
	public Menu[] getMenu() {
		MenuService mock = 
			(MenuService) ScaMock.getInstance().get( MenuService.class );
		return mock.getMenu();
	}
 
	/**
	 * Delegate the call to the mock.
	 * @see MenuService#getPrice(Menu)
	 */
	public double getPrice( Menu menu ) {
		MenuService mock = 
			(MenuService) ScaMock.getInstance().get( MenuService.class );
		return mock.getPrice( menu );
	}
}


Notice that the service to test is present as a reference in this class. Let's now take a look at the methods of this class.

All the references' operations have the same behaviour. They get a mock object from a singleton class and delegate the call to this object.

The main method, runScaTest, creates a TestSuite, instanciates a TestCase and register all the methods which begins with "test". It then runs this TestSuite and returns the result as a SerializableTestResult.


The last generated class is a TestCase (only its skeleton is generated, testing methods have to be written by the user).

// @ScaTest( composite="{targetNamespace}compositeName" name="componentA" )
// Annotation not yet defined. Attributes to review.
// Useful only inside an Eclipse project, when using a builder to synchronize business and test code.
public class RestaurantServiceComponentTC extends TestCase {
	// Mocks.
	/** The mock for BillService. */
	private BillService billService = 
		(BillService) ScaMock.getInstance().get( BillService.class );
 
	/** The mock for MenuService. */
	private MenuService menuService = 
		(MenuService) ScaMock.getInstance().get( MenuService.class );
 
	// Referenced services.
	/**  */
	private RestaurantService restaurantService;
 
 
	/**
	 * Constructor that stores an instance of the services to test.
	 * In a generation approach, this constructor should have as parameters the name
	 * of the test method to run (fName), and an instance of all the referenced services
	 * of the element to test (component or composite).
	 * 
	 * @param fName the name of the method to run in this TestCase.
	 * @param restaurantService the restaurantService to set.
	 */
	public RestaurantServiceComponentTC( String fName, RestaurantService restaurantService ) {
		super( fName );
		this.restaurantService = restaurantService;
	}
 
	/**
	 * Reset the behavior of the mock objects during each test.
	 */
	@Override
	protected void setUp() throws Exception {
		reset( billService );
		reset( menuService );
	}
 
	/* TODO: write testing methods after this comment.
	 * Testing method names should begin with "test". 
	 */
}


This skeleton has two attributes which are the mocked references. It has a third attribute which the service of the component to test (set in the constructor). Eventually, we configure the setUp method to reset the behavior of the mocks between each run.


Until now, all of this can be generated by a tool. In this case, the only remaining thing for the user is writing test methods.

Do not expect the following tests to be smart. They were written to show what could be (and not what should be) done to test such an application.

// @ScaTest( composite="{targetNamespace}compositeName" name="componentA" )
// Annotation not yet defined. Attributes to review.
// Useful only inside an Eclipse project, when using a builder to synchronize business and test code.
public class RestaurantServiceComponentTC extends TestCase {
	// Mocks.
	/** The mock for BillService. */
	private BillService billService = 
		(BillService) ScaMock.getInstance().get( BillService.class );
 
	/** The mock for MenuService. */
	private MenuService menuService = 
		(MenuService) ScaMock.getInstance().get( MenuService.class );
 
	// Referenced services.
	/**  */
	private RestaurantService restaurantService;
 
 
	/**
	 * Constructor that stores an instance of the services to test.
	 * In a generation approach, this constructor should have as parameters the name
	 * of the test method to run (fName), and an instance of all the referenced services
	 * of the element to test (component or composite).
	 * 
	 * @param fName the name of the method to run in this TestCase.
	 * @param restaurantService the restaurantService to set.
	 */
	public RestaurantServiceComponentTC( String fName, RestaurantService restaurantService ) {
		super( fName );
		this.restaurantService = restaurantService;
	}
 
	/**
	 * Reset the behavior of the mock objects during each test.
	 */
	@Override
	protected void setUp() throws Exception {
		reset( billService );
		reset( menuService );
	}
 
	/* TODO: write testing methods after this comment.
	 * Testing method names should begin with "test". 
	 */
 
	/**
	 * Test 1.
	 * Check the getMenus() operation of the service.
	 */
	public void test1() {
		Menu[] expectedMenus = new Menu[] {
				new MenuImpl( 0, "Hamburger with french fries" ),
				new MenuImpl( 1, "Salad with eggs and olives" )
		};
 
		// We are going to call getMenus() on the service.
		// We know its implementation makes one call to menuService.getMenu().
		// Mock menuService: expect a call to getMenus, and return a list of menus.
		expect( menuService.getMenu()).andReturn( expectedMenus );
		replay( menuService );
 
		// Call getMenus() and check the result.
		Menu[] menus = restaurantService.getMenus();		
		assertEquals( 2, menus.length );
 
		// Is there a hamburger in the menu ?
		boolean thereIsHamburger = false;
		for( Menu menu : menus )
			thereIsHamburger = thereIsHamburger || menu.printMenu().toLowerCase().contains( "hamburger" );
		assertTrue( thereIsHamburger );
	}
 
	/**
	 * Test 2.
	 * Check the getBill(Menu) operation of the service.
	 * We here forget to define the behavior of billService. This test should fail.
	 * Notice that it will not throw an EasyMock exception since we do not replay the billService mock.
	 */
	public void test2() {
		Menu expectedMenu = new MenuImpl( 0, "Hamburger with french fries" );
 
		// We are going to call getBill(Menu) on the service.
		// We know its implementation makes one call to menuService.getPrice(Menu).
		// Mock menuService: expect a call to getPrice(Menu), and return 20.0
		expect( menuService.getPrice( expectedMenu )).andReturn( 20.0 );
		replay( menuService );
 
		// Any other call to this reference should throw an exception, not managed here.
 
		// Call getBill(Menu) and check the result.
		double bill = restaurantService.getBill( expectedMenu );		
		assertEquals( 25.0, bill );
	}
 
	/**
	 * Test 3.
	 * Check the getBill(Menu) operation of the service.
	 * Same test than test2, with another exception. This test should be an error.
	 * This time, we replay the billService mock but we still do not foresee the use of a operation.
	 * 
	 * It shows that the user has to replay the mock references in every method and for every mock.  
	 */
	public void test3() {
		Menu expectedMenu = new MenuImpl( 0, "Hamburger with french fries" );
 
		// We are going to call getBill(Menu) on the service.
		// We know its implementation makes one call to menuService.getPrice(Menu).
		// Mock menuService: expect a call to getPrice(Menu), and return 20.0
		expect( menuService.getPrice( expectedMenu )).andReturn( 20.0 );
		replay( menuService );
 
		// Any other call to this reference should throw an exception, not managed here.
 
		// We also know its implementation makes one call to billService.getBill(double).
		replay( billService );
 
		// Any other call to this reference should throw an exception, not managed here.
 
		// Call getBill(Menu) and check the result.
		double bill = restaurantService.getBill( expectedMenu );		
		assertEquals( 25.0, bill );
	}
 
	/**
	 * Test 4.
	 * Check the getBill(Menu) operation of the service.
	 * Same test than test2 but we here define all the behaviors.
	 */
	public void test4() {
		Menu expectedMenu = new MenuImpl( 0, "Hamburger with french fries" );
 
		// We are going to call getBill(Menu) on the service.
		// We know its implementation makes one call to menuService.getPrice(Menu).
		// Mock menuService: expect a call to getPrice(Menu), and return 20.0
		expect( menuService.getPrice( expectedMenu )).andReturn( 20.0 );
		replay( menuService );
 
		// Any other call to this reference should throw an exception, not managed here.
 
		// We also know its implementation makes one call to billService.getBill(double).
		// Mock billService: expect a call to getBill(double), and return 25.0
		expect( billService.getBill( 20.0 )).andReturn( 25.0 );
		replay( billService );
 
		// Any other call to this reference should throw an exception, not managed here.
 
		// Call getBill(Menu) and check the result.
		double bill = restaurantService.getBill( expectedMenu );		
		assertEquals( 25.0, bill );
	}
 
	/**
	 * Test 5.
	 * Check the getBill(Menu) operation of the service.
	 * Same test than test4 but we are here less strict on mock calls.
	 * This illustrates the use of EasyMock.
	 * 
	 * For more details about available features of EasyMock, check their documentation at 
	 * {@linkplain http://www.easymock.org/EasyMock2_4_Documentation.html}.
	 */
	public void test5() {
		Menu expectedMenu = new MenuImpl( 0, "Hamburger with french fries" );
 
		// Mock menuService: expect a call to getPrice(Menu), and return 20.0	
		// Change: we can call getPrice an undetermined number of times.
		// Change: we can call getPrice with any Menu parameter.
		expect( menuService.getPrice( isA( Menu.class ))).andReturn( 20.0 ).anyTimes();
		replay( menuService );
 
		// Any other call to this reference should throw an exception, not managed here.
 
		// Mock billService: expect a call to getBill(double), and return 25.0
		// Change: return 25.0 each time 20.0 is given as argument.
		// And return -1.0 then for any argument different from 20.0.
		expect( billService.getBill( 20.0 )).andReturn( 25.0 ).anyTimes();
		expect( billService.getBill( not( eq( 20.0 )))).andStubReturn( -1.0 );
		replay( billService );
 
		// Any other call to this reference should throw an exception, not managed here.
 
		// Call getBill(Menu) and check the result.
		double bill = restaurantService.getBill( expectedMenu );		
		assertEquals( 25.0, bill );
	}
}

In these test methods, we defined the behavior of the "references" (the mock objects) by using EasyMock methods, and we check assertions on service operation results. The difficulty here is how you can foresee the behavior of the references. It requires you to know how the references are used in the implementation of the component. From this point of view, you do not test only the service service contract but also the way and how the service is implemented. We are not really in a Test Driven Approach.


Runnning the Tests

We now have written a TestCase for SCA and we need to run it on a SCA platform. This example has been run manually on a Tuscany runtime (i.e. Tuscany jars were added manualy to the project class path). But there is no reason for this application to not work with Fabric3 or Frascati. In the future, SCA platforms will be registered as servers in Eclipse (like J2EE servers - registration through the SOAS sub-project of STP). For the moment, Tuscany, Fabric3 and PEtALS (with its SCA service engine based on Frascati) are expected to be supported before the end of the year.

Running test is made by right-clicking on the TestCase to run and by selecting SCA > Run > platform, where platform is a registered runtime server. It results in the deployment of the tester application and in the generation of a client to call this application.

The server...

/**
 * Deploy the SCA test composite on a Tuscany runtime.
 */
public class ScaTestServer {
 
	public static void main( String[] args ) throws IOException {
		System.out.println( "Starting Test Application" );
		SCADomain scaDomain = SCADomain.newInstance( "TestRestaurantServiceComponent.composite" );
 
		System.out.println( "Press any key to Exit" );
		System.in.read();
		scaDomain.close();
		System.out.println( "Done." );
		System.exit( 0 );
	}
}

... and the client.

/**
 * A simple test client to call the SCA test application via RMI and print the result of the test.
 */
public class ScaTestClient {
 
	public static void main( String[] args ) throws MalformedURLException, RemoteException, NotBoundException {
		RunTestService scaTestRunner = (RunTestService) Naming.lookup( "//localhost:8098/TestSca" );
		SerializableTestResult tc = scaTestRunner.runScaTest();
		System.out.println( tc.toString());
	}
}


When both of these classes are run, it prints:

====== Test Result ======
=========================
5 tests were run.

Failure(s): 1
+ test2(org.eclipse.stp.sca.test.sample.impl.RestaurantServiceComponentTC): expected:<25.0> but was:<0.0>

Error(s): 1
+ test3(org.eclipse.stp.sca.test.sample.impl.RestaurantServiceComponentTC): null

==> Test failed.


In Eclipse, results should rather be displayed in the usual JUnit view instead of being printed in the console. The example of this application is available in the SVN of the SCA sub-project of STP (project org.eclipse.stp.sca.test.sample in the incubation directory).

Copyright © Eclipse Foundation, Inc. All Rights Reserved.