This blog is about Integration Gateway in SAP Mobile Platform 3.0 (SMP)

This last part of our tutorial is an optional step to show how we can test our DataProvider bundle automatically.

See part 1 for creation of the bundle.

In our example scenario it makes sense to have automated tests, even more than usually.

Reason: in our DataProvider bundle, we’re exposing an API that we’re using from our OData service project.

During development, we have to build the bundle, deploy to SMP, and we have to generate&deploy the OData service, which is a little tedious.

Therefore, it makes sense to use this API prior to deployment to SMP

Furthermore, we can ensure with our automated tests that the API works as expected and that changes won’t break the OData service.

Note:

This test bundle is not expected to be deployed to SMP

Overview

Part I

1. Prepare SMP server

2. Prepare Eclipse

3. Create the OSGi bundle

    3.1. Create Plug-In project

    3.2. Implement DataProvider class

    3.3. Implement Activator class

    3.4. Deploy the bundle to SMP

    3.5. Verify the deployed bundle


Part II

4. Create the OData service

    4.1. Implement the QUERY operation

    4.2. Adding $skip and $top capability

    4.3. Implement the READ operation

5. Test the OData service

Part III

6. Debug the Java code

    6.1. Start SMP server in debug mode

    6.2. Connect from Eclipse to SMP

    6.3. Debug the Java code in Eclipse

7. Summary

8. Links

Part IV

9. Test the API automatically

    9.1. Create test fragment

    9.2. Create JUnit tests

    9.3. Run the tests

    9.4. Summary

9. Test the API automatically

What are we going to do?

– We want to create a test class with methods that call the DataProvider just like it will be done by the OData service

– We want these test methods to be implemented as jUnit tests

– We want to run all jUnit tests with one click

– We want to have the test code separated from the DataProvider bundle, such that the test code isn’t deployed to SMP

9.1. Create test fragment

Create Fragment project

A Fragment in terms of OSGi is like a special kind of Bundle. It has an own ID and own content. The difference is that it requires a host, which is another bundle.

As such, a fragment cannot live alone.

On the other side, the host doesn’t require the fragment.

Therefore, a typical use case for a fragment is test code.

A fragment shares the classloader of the host bundle, as such it has the great advantage that the test code can access classes that are not meant to be public.

In the following section, we will create a fragment in Eclipse that defines our DataProvider bundle as host.

In Eclipse main menu, go to

File->New->Project->Plug-in Development->Fragment Project

Press “Next” and give a project name, ideally the name of the host bundle with suffix “test”.

In our example:

com.example.dataprovider.test

After pressing “Next”, provide the data as desired.

The Host Plug-in is the DataProvider bundle that we’ve created in the first part################## of our tutorial:

com.example.dataprovider

Press “Finish” and don’t change to “Plugin Development perspective”.

The project skeleton is generated and the editor for the MANIFEST.MF file is opened.

Add dependencies

In our fragment, we inherit the dependencies of the host bundle, so we don’t need to add the dependency to Olingo and Integration Gateway api libraries.

However, since we’re acting like the OData service implementation project, we have to add some additional dependencies, which we’ll need to get our test code running:


Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Test fragment for Example DataProvider
Bundle-SymbolicName: com.example.dataprovider.test
Bundle-Version: 1.0.0
Bundle-Vendor: ExampleVendor
Fragment-Host: com.example.dataprovider;bundle-version="1.0.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Require-Bundle: olingo-odata2-core;bundle-version="2.0.4",
com.sap.it.public.api;bundle-version="1.0.0",
org.apache.camel.camel-core;bundle-version="2.14.1",
com.sap.gw.rt.script.engine.api;bundle-version="1.14.1",
com.sap.it.commons;bundle-version="1.14.0",
com.sap.it.commons.logging.slf4j;bundle-version="1.14.0",
org.slf4j.api;bundle-version="1.7.2"












Note:

The versions shown above correspond to SMP SP10

Save the editor

Adapt build path

We want to write JUnit tests, therefore we need to compile against the JUnit framework library.

Remember that we changed the target platform in Eclipse (tutorial part 1)?

The fragment that we’ve created compiles against SMP, as we’ve configured in part 1, but on SMP server we don’t expect the JUnit-test-framework to be available, so we have to workaround the situation.

There are several options, I think the easiest solution is:

JUnit is contained in our Eclipse IDE, and even if we don’t change the target platform, we can still add the JUni lib manually to the build path of our fragment project.

Select your fragment project and from the context menu choose

Build Path -> Configure Build Path…

The project properties dialog is displayed and the “Java Build Path” property is selected.

On the right pane, select the “Libraries” tab.

Press the button “Add Library…”

On the subsequent popup, select “JUnit”, press “Next” and choose “JUnit 4” as library version.

After pressing “Finish”, this library is added to the build path of your project.

This (Eclipse-) library contains the junit jarfile

9.2. Create jUnit tests

Now we can start writing our test code.

Generate JUnit Test Case

Select your fragment project and from the context menu, choose

New->Other->JUnit->JUnit Test Case

Select JUnit 4, specify the values as shown below and the “Class under test” is the only class that we’ve created in part 1, the DataProvider

After pressing “Next”, you can select which methods of the class under test should be tested.

We select all the API-methods that are meant to be used by the OData service:

After pressing “Finish”, the Java class and the methods are generated.

Create helper methods

Before we start writing the test code, we create a helper method that creates a Message object which we need for the DataProvider instance.

This is the code:


private Message createMessage(Integer topNumber, Integer skipNumber, String productID){
    UriInfoImpl uriInfo = new UriInfoImpl();
    // required for testTop
    if(topNumber != null){
        uriInfo.setTop(topNumber);
    }
    // required for testSkip
    if(skipNumber != null){
        uriInfo.setSkip(new Integer(skipNumber));
    }
    // required for testRead
    if(productID != null){
        KeyPredicateImpl keyPredicate = new KeyPredicateImpl(productID, null);
        List<KeyPredicate> keyPredicates = new ArrayList<KeyPredicate>();
        keyPredicates.add(keyPredicate);
        uriInfo.setKeyPredicates(keyPredicates);
    }
    Message message = new Message();
    message.setHeader("UriInfo", uriInfo);
    return message;
}










After pasting it into your DataProviderTest class, you’ll see error markers.

Press Ctrl + Shift + O in order to let Eclipse add the required imports.

But the code will still have error markers.

The reason is that the code uses internal classes of the Olingo core plugin, which is not public API, as such not visible from our fragment.

For our test code, we don’t mind using internal classes, so we go ahead and remove the acess rule check:

Select the fragment project and from the context menu, choose

Properties->Java Compiler->Errors/Warnings

On the properties page, select “Enable project specific settings”, then assign “Warning” to “Forbidden reference (access rule)” and press OK.

With this setting, we allow ourselves to use the restricted internal classes.

However, it is enabled only for the test-project, because we’ve applied the setting on project-level, not for the whole Eclipse.

Now that we can use the internal class, we have to add the required import statement to our DataProviderTest class:


import org.apache.olingo.odata2.core.uri.*;









After that, we can save and the error markers will be gone.

If the warnings in the editor bother you too much, you can either change the above setting to “Ignore”, or you can add the following annotation to the method:


@SuppressWarnings("restriction")
private Message createMessage(Integer topNumber, Integer skipNumber, String productID){









We need another helper method, which creates some dummy data, as such allows us to test independent from the backend data.


private List<Map<String, String>> getTestData(int prodAmount){
    List<Map<String, String>> productList = new ArrayList<Map<String, String>>();
    // create some sample product entities as HashMaps
    for (int i = 1; i <= prodAmount; i++) {
        String index = Integer.toString(i);
        Map<String, String> productMap = new HashMap<String, String>();
        productMap.put("ID", index);
        productMap.put("Name", "TestName" + index);
        productMap.put("Description", "TestDescription" + index);
        productList.add(productMap);
    }
    return productList;
}









Implement testGetAllProducts()

In the present tutorial, we’re really only doing the simplest approach to showcase the testing.

So for the testGetAllProducts, we only invoke the respective method of the DataProvider class.

In a productive environment, the DataProvider class would allow injection in order to support the test, and we would add more fine-granular checks and asserts


    @Test
    public void testGetAllProducts() {
        Message message = createMessage(null, null, null);
        DataProvider dataProvider = new DataProvider(message);
        List<Map<String, String>> allProducts = dataProvider.getAllProducts();
        assertNotNull("Calling getAllProducts shouldn't return null", allProducts);
    }









Implement testGetProduct()

In this test, we simulate that a READ URL for the product with ID 1 has been executed and we check that the product has been found

Then we simulate a READ for productID 6, which doesn’t exist, and we check that the result is null.


    @Test
    public void testGetProduct() {
        Message message = createMessage(null, null,"1");
        // start the positive test
        DataProvider dataProvider = new DataProvider(message);
        Map<String, String> product = dataProvider.getProduct();
        assertNotNull("Product expected to exist", product);
        // negative test
        Message invalidMessage = createMessage(null, null,"6"); // this ID doesn't exist
        // start the positive test
        DataProvider dataProvider2 = new DataProvider(invalidMessage);
        Map<String, String> productNull = dataProvider2.getProduct();
        assertNull("Expected to be null", productNull);
    }









Implement testHandleSkip()

In this test method, we simulate a URL with $skip=1

We create test data with 4 Products

Then we check that the result list contains 3 entries


    @Test
    public void testHandleSkip() throws Exception{
        Message message = createMessage(null, 1, null);
        List<Map<String, String>> allProducts = getTestData(4);
        // start the test
        DataProvider dataProvider = new DataProvider(message);
        List<Map<String,String>> skipProducts = dataProvider.handleSkip(allProducts);
        assertEquals(3, skipProducts.size());
    }








Implement testHandleTop()

In this test method, we simulate a URL with $top=1

We create test data with 4 Products

Then we check that the result list contains 1 entry


    @Test
    public void testHandleTop() throws Exception{
        Message message = createMessage(1, null, null);
        List<Map<String, String>> allProducts = getTestData(4);
        // start the test
        DataProvider dataProvider = new DataProvider(message);
        List<Map<String,String>> topProducts = dataProvider.handleTop(allProducts);
        assertEquals(1, topProducts.size());
    }








The full source code


package com.example.dataprovider.test;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.olingo.odata2.api.uri.KeyPredicate;
import org.apache.olingo.odata2.core.uri.KeyPredicateImpl;
import org.apache.olingo.odata2.core.uri.UriInfoImpl;
import org.junit.Test;
import com.example.dataprovider.DataProvider;
import com.sap.gateway.ip.core.customdev.util.Message;
public class DataProviderTest {
    @Test
    public void testGetAllProducts() {
        Message message = createMessage(null, null, null);
        DataProvider dataProvider = new DataProvider(message);
        List<Map<String, String>> allProducts = dataProvider.getAllProducts();
        assertNotNull("Calling getAllProducts shouldn't return null", allProducts);
    }
    @Test
    public void testGetProduct() {
        Message message = createMessage(null, null,"1");
        // start the positive test
        DataProvider dataProvider = new DataProvider(message);
        Map<String, String> product = dataProvider.getProduct();
        assertNotNull("Product expected to exist", product);
        // negative test
        Message invalidMessage = createMessage(null, null,"6"); // this ID doesn't exist
        // start the positive test
        DataProvider dataProvider2 = new DataProvider(invalidMessage);
        Map<String, String> productNull = dataProvider2.getProduct();
        assertNull("Expected to be null", productNull);
    }
    @Test
    public void testHandleSkip() throws Exception{
        Message message = createMessage(null, 1, null);
        List<Map<String, String>> allProducts = getTestData(4);
        // start the test
        DataProvider dataProvider = new DataProvider(message);
        List<Map<String,String>> skipProducts = dataProvider.handleSkip(allProducts);
        assertEquals(3, skipProducts.size());
    }
    @Test
    public void testHandleTop() throws Exception{
        Message message = createMessage(1, null, null);
        List<Map<String, String>> allProducts = getTestData(4);
        // start the test
        DataProvider dataProvider = new DataProvider(message);
        List<Map<String,String>> topProducts = dataProvider.handleTop(allProducts);
        assertEquals(1, topProducts.size());
    }
    /* HELPER */
    // create a dummy message object. Access to internal packages is required
    private Message createMessage(Integer topNumber, Integer skipNumber, String productID){
        UriInfoImpl uriInfo = new UriInfoImpl();
        // required for testTop
        if(topNumber != null){
            uriInfo.setTop(topNumber);
        }
        // required for testSkip
        if(skipNumber != null){
            uriInfo.setSkip(new Integer(skipNumber));
        }
        // required for testRead
        if(productID != null){
            KeyPredicateImpl keyPredicate = new KeyPredicateImpl(productID, null);
            List<KeyPredicate> keyPredicates = new ArrayList<KeyPredicate>();
            keyPredicates.add(keyPredicate);
            uriInfo.setKeyPredicates(keyPredicates);
        }
        Message message = new Message();
        message.setHeader("UriInfo", uriInfo);
        return message;
    }
    // create some sample test data, in order to not depend on backend data
    private List<Map<String, String>> getTestData(int prodAmount){
        List<Map<String, String>> productList = new ArrayList<Map<String, String>>();
        // create some sample product entities as HashMaps
        for (int i = 1; i <= prodAmount; i++) {
            String index = Integer.toString(i);
    
            Map<String, String> productMap = new HashMap<String, String>();
            productMap.put("ID", index);
            productMap.put("Name", "TestName" + index);
            productMap.put("Description", "TestDescription" + index);
    
            productList.add(productMap);
        }
        return productList;
    }
}






9.3. Run the tests

From the Eclipse main menu, choose Run->Run Configurations

In the Run Configurations dialog, select “JUnit” and from the context menu choose “New”

In the details pane, give an arbitrary name for your Run Configuration.

Select “Run all tests…” and make sure that your test fragment project is entered (otherwise press “Search” to enter it)

The “Test runner” has to be specified as “JUnit 4”

Note:

You can also choose to run only one single test.

This is desired, when not all test methods are finalized yet or if running all test method would take too long time, etc

After pressing “Run”, the selected tests are executed.

The results are presented in the JUnit View. If this is not visible, it has to be opened via Window->Show View -> Other -> Java -> JUnit

Note:

In case of problems, a JUnit test can also be debugged, which is done by choosing “Debug” instead of “Run”

Create Test Suite

Let’s quickly cover one last topic.

Usually, you’ll have more than one class and more than one scenario to be covered.

In such cases, you’ll have lots of test case classes and it makes sense to aggregate them in a so-called Test Suite.

Like that, you can run all the test methods in all the test cases with one click.

In Eclipse, select your test fragment project and choose from the context menu

New->Other->JUnit->JUnit Test Suite

After pressing “Next”, make sure that “JUnit 4” is selected, enter a package and adapt the Name if desired.

The one and only test class that we have, is already selected, so we can press “Finish”

The generated Test Suite class is opened, we don’t need to change anything in our example.

A quick way to run the tests is to directly click into the editor and from the context menu choose Run As->JUnit Test


Now all the tests in the suite are executed.

Again, the result can be viewed in the JUNit view, this time the root node is the suite:

9.4. Summary

In the present blog we’ve shown how we can leverage JUnit for reducing develop-deploy-test-turnarounds.

In our JUnit test-code, we act like the OData service that calls the DataProvider-API and as such we can quickly check if it works as expected and we can enhance the DataProvider bundle without the need of deploying it to SMP.

We’ve taken advantage of the Fragment-concept to develop the JUnit tests.

We’ve reached the end of this little tutorial in which I’ve illustrated how I like to work in the area of OData services based on Integration Gateway in SMP 3.0

My points are:

– scripting is done in the OData project, but whenever it gets complex, move the code to a separate bundle

– advantages are the enhanced coding support in the editor and the debugging capabilities.

In part 1 of this tutorial, we’ve created the bundle and we’ve written all the code required to provide the data and to handle the requests.

At the end, we’ve deployed that bundle to the SMP server and verified that it works as expected

In part 2, we’ve created the OData service implementation project and we’ve delegated all the coding to the bundle. We’ve specified a dependency to that bundle, we’ve deployed the OData service project to SMP server and we’ve verified that the OData service works as expected

In part 3, we’ve learned how to debug the code that we’ve written in the bundle

In part 4 (this page), we’ve learned how to test the bundle without deploying it to the server

All these 4 blogs together should significantly enhance the development productivity.

I hope you agree and you’ve enjoyed it!

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply