Develop Advanced Extensions with SAP Cloud SDK

Mocking SAP S/4HANA Calls

Objectives
After completing this lesson, you will be able to:

After completing this lesson, you will be able to:

  • Explain mocking capabilities of SAP Cloud SDK using MockUtil class

Mocking SAP S/4HANA Calls Without an SAP S/4HANA System

In automated unit testing, it is sometimes necessary to use objects or procedures that look and behave like their release-intended counterparts, but are actually simplified versions that reduce complexity and facilitate testing. Such procedures are called test doubles. Mocking refers to the idea of providing these test doubles instead of the real implementation during tests. This helps in reducing the testing effort in cases where testing could be too cumbersome, or it is not possible to instantiate the entire program.

Furthermore, as we discussed with the testing pyramid, doing more unit tests is preferable during the development of the application as it forces application developers to write more testable application code and apply separation of concerns more thoroughly, which usually leads to more maintainable and understandable code. Using mocking and the tools provided by SAP Cloud SDK, we can test a small portion of the code without the need for a real SAP S/4HANA back-end system. This provides a very convenient way to test the overall functionality.

The MockUtil is a mocking utility class; it is provided by the SAP Cloud SDK. The MockUtil class helps create the mock instances required to simulate instances. The MockUtil class provided by the SAP Cloud SDK can also deal with connectivity to an SAP S/4HANA back-end.

To implement mocking using the MockUtil class (in Java), you need to:

  • Create a new test file under your project location – <project>/unit-tests/src/test/java
  • Initialize the mocking facility MockUtil provided by the SAP Cloud SDK
  • Declare an initialization method beforeClass() which is annotated with @BeforeClass. This means that this method is called exactly once before the execution of all other test methods. Inside the method, all the required SAP Cloud Platform mocks are initialized (tenant, user, etc)
  • Inside the method beforeClass(), mock a dummy SAP S/4HANA destination
  • Inside the method beforeClass(), initialize the testing parameters
  • Lastly add another method inside the newly created file to do the actual first testing. Inside this method we do the actual mocking by defining under which condition what should happen

Code Sample to Mock

Consider our existing Business Partner Address Manager application. In the GetAllBusinessPartnersCommand command, the run() method does a type-safe OData call to the BusinessPartner API of the SAP S/4HANA system. This fetches the business partner details and address. To view the code, navigate to your project and choose address-managerapplicationsrcmainjava.

When it comes to mocking, this piece of code is tested locally rather than against a real SAP S/4HANA system. To achieve this, we add a new test command under the unit-tests directory. To access the unit-tests directory, choose address-managerunit-testssrctestjava.

Example of Test Command to Add

The following is an example of the code required to add a new test command called GetBusinessPartnerCommandMockTest:

Code snippet
import com.sap.cloud.sdk.odatav2.connectivity.ODataException;
import com.google.common.collect.Lists;
import com.sap.cloud.sdk.testutil.MockUtil;
import org.mockito.junit.MockitoJUnitRunner;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

public class GetBusinessPartnerMockedTest
{
private static final MockUtil mockUtil = new MockUtil();
private static BusinessPartner tom;
private static BusinessPartner kathy;

@BeforeClass
public static void beforeClass() throws Exception {
mockUtil.mockDefaults();
mockUtil.mockDestination("ErpQueryEndpoint", URI.create(""));

tom = new BusinessPartner();
tom.setFirstName("Tom");

kathy = new BusinessPartner();
kathy.setFirstName("Kathy");
}

@Test
public void testGetAnyBusinessPartner() throws Exception
{
final BusinessPartnerService service = Mockito.mock(BusinessPartnerService.class, RETURNS_DEEP_STUBS);

when(service.getAllBusinessPartner()
.filter(any(ExpressionFluentHelper.class))
.select(any(EntityField.class))
.execute(any(ErpConfigContext.class)))
.thenReturn(Lists.newArrayList(tom, kathy));

final List businessPartnerList = new GetAllBusinessPartnersCommand(new ErpConfigContext(), service).execute();

assertEquals(2, businessPartnerList.size());
assertEquals("Tom", businessPartnerList.get(0).getFirstName());
assertEquals("Kathy", businessPartnerList.get(1).getFirstName());
}
}
Copy code

The following table explains what happens in this piece of code:

Lines of CodeExplanation
Code snippet
private static final MockUtil mockUtil = new MockUtil();
Copy code
It initializes the mocking facility MockUtil provided by the SAP Cloud SDK
Code snippet
private static BusinessPartner tom;
private static BusinessPartner kathy;
Copy code
It declares two test business partners: Tom and Kathy.
Code snippet
@BeforeClass
public static void beforeClass() throws Exception {
mockUtil.mockDefaults();
mockUtil.mockDestination("ErpQueryEndpoint", URI.create(""));
Copy code
It initializes all the required SAP BTP mocks inside the beforeClass() method.

This includes mocking a dummy SAP S/4HANA destination. It is annotated with @BeforeClass, which means that this method is called exactly once before all the other test methods are executed.

Code snippet
tom = new BusinessPartner();
tom.setFirstName("Tom");

kathy = new BusinessPartner();
kathy.setFirstName("Kathy");
Copy code
It initializes two test business partners with their intended first names
Code snippet
final BusinessPartnerService service = Mockito.mock(BusinessPartnerService.class, RETURNS_DEEP_STUBS);
Copy code
The actual testing happens inside the testGetAnyBusinessPartner() method.

Inside this method, we first create a mock (proxy object) of the business partner service interface that replaces the actual SDK implementation with mocked methods. As we want to mock the entire fluent API, including all its delegating classes and not just the interface itself, we also need to provide the RETURNS_DEEP_STUBS option.

Code snippet
when(service.getAllBusinessPartner()
.filter(any(ExpressionFluentHelper.class))
.select(any(EntityField.class))
.execute(any(ErpConfigContext.class)))
.thenReturn(Lists.newArrayList(tom, kathy));
Copy code
It contains the mocking.

The when-then statements are used to determine what should happen under what condition.

In the above lines of code, we simply define that whenever the BusinessPartner API is called, regardless of any condition applied, such as filter condition and selection criteria, it will always return Tom and Kathy.

Code snippet
final List businessPartnerList = new GetAllBusinessPartnersCommand (new ErpConfigContext(), service).execute();

assertEquals(2, businessPartnerList.size());
assertEquals("Tom", businessPartnerList.get(0).getFirstName());
assertEquals("Kathy", businessPartnerList.get(1).getFirstName());
Copy code
It initializes the GetAllBusinessPartnersCommand and passes the mocked business partner service into our production code command.

When this step is complete, the mock test of the code can be carried out.

Save progress to your learning plan by logging in or creating an account