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

SCA/SCA Component/SCA Testing SCA Applications Unit Example

< SCA

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).

Back to the top