mockA tutorial – How to create mocks

The previous blog post dealt with the question how fakes can be created using the new open source mocking framework mockA.
Fakes are mocked instances that return specific values when certain methods are called. In comparison to mocks, they do not validate if certain methods have been called or not.
The creation of fakes does not differ from the creation of mocks when you use mockA. Take a look at the setup routine of the unit test report ZTEST_CL_MOCKA_FLIGHT_OBSERVER that is ships with the framework.
In this unit test, a sample flight observer is to be tested. The flight observer depends on two other components, which are an object that provides some flight information as well as an object that processes alerts on specific late flights. The flight observer is determined to get the delay status of some sample flight data and delegate the creation of some alerts to the alert processor conditionally. While the flight information object serves as fake only, as it returns only sample data for specific flights (that is, method inputs), the alert processor serves as mock object that is subject to unit test assertions.
Please note, that MO_ALERT_PROCESSOR_MOCKER is a member attribute which can be used for later use. The created mock object does not need to return any values. This means, the object is only created but no method output is specified.
** Member attributes
* DATA mo_alert_processor TYPE REF TO ZIF_MOCKA_FLIGHT_ALERT_PROCESS .
* DATA mo_alert_processor_mocker TYPE REF TO ZIF_MOCKA_MOCKER.

* create an empty alert backend (we just need to track the number of method calls)
mo_alert_processor_mocker = ZCL_MOCKA_MOCKER=>ZIF_MOCKA_MOCKER~mock( iv_interface = 'ZIF_MOCKA_FLIGHT_ALERT_PROCESS' ).
* this call creates the alert processor mockup
mo_alert_processor ?= mo_alert_processor_mocker->generate_mockup( ).

If a certain method has been invoked on the mock object MO_ALERT_PROCESSOR can be checked later by the following call on its creator.
DATA lv_has_been_called TYPE apa_bool.
lv_has_been_called =
mo_alert_processor_mocker->has_method_been_called( 'ALERT_DELAY' ).

This kind of verification is usually needed in the assert-section of the unit test. Alternatively, you can access the exact method call count by calling this method:
DATA lv_method_call_count TYPE i.
lv_method_call_count =
mo_alert_processor_mocker->method_call_count( 'ALERT_DELAY' ).

And that’s it. Besides the verification of a method call count, no other validation can be carried out against mock objects yet. In future releases, the verification if a specific method has been called with specific values could be implemented. This is one of the top priority functional gaps which exist for the framework right now.

mockA tutorial – How to create fakes

Introduction

mockA, an open source ABAP mocking framework has been released recently. The today’s blog post gives you a brief introduction of the features of mockA.
Mocking Frameworks are usually used in unit tests. Their main task is the creation of test double instances more easily than e.g. manually creating local test classes which implement an interface from which the system under test depends.
This tutorial will show you how to create so called “fakes”. Fakes usually show some kind of behavior when called. In contrast “Mocks” usually also verify that some interaction with them took please. Mocks will be subject to a future blog post.

Where to get mockA

You can download the mocking framework from Github.
Install the daily build using the latest SAPLink release.
If you like it, feel free to participate in the development of the tool.

Mocking methods with returning parameters

Interface ZIF_MOCKA_IS_IN_TIME_INFO is the interface which is to be mocked in this tutorial. The interface ships with the release of mockA. Please note that no global class implements this interface. Nevertheless, we will create local classes at runtime which allow us to create objects which can eventually be called within the same report.
Before we can create a mock object, we need to tell mockA which interface is subject to mocking:
DATA lo_mocker TYPE REF TO zif_mocka_mocker.
lo_mocker = zcl_mocka_mocker=>zif_mocka_mocker~mock( 'zif_mocka_is_in_time_info' ).

In the second step, we need to tell the mocker, which method will be mocked. Please note, that you can easily use the fluent API be directly telling the mocker, which input should lead to which method’s output:
lo_mocker->method( ‘GET_DELAY’ )->with( i_p1 = ‘LH’ i_p2 = ‘123’ i_p3 = ‘20131022’ )->returns( 30 ).

In the end, the local class implementing ZIF_MOCKA_IS_IN_TIME_INFO can be created and the mock object will be instantiated.
DATA lo_is_in_time_info TYPE REF TO zif_mocka_is_in_time_info.lo_is_in_time_info ?= lo_mocker->generate_mockup( ).

Calling the mock object with some registered method input will return the specified output.
DATA lv_delay TYPE int4.
"will return lv_delay = 30
lv_delay = lo_is_in_time_info->get_delay( iv_carrid = 'LH' iv_connid = '123' iv_fldate = '20131022' ).

Please note, that registering the same method call pattern twice leads to different method output when the method is called multiple times with parameters that fit to the registered pattern:
lo_mocker = zcl_mocka_mocker=>zif_mocka_mocker~mock( 'zif_mocka_is_in_time_info' ).
lo_mocker->method( 'GET_DELAY' )->with(
i_p1 = 'LH' i_p2 = '123' i_p3 = '20131022'
)->returns( 30 ).
lo_mocker->method( 'GET_DELAY' )->with(
i_p1 = 'LH' i_p2 = '123' i_p3 = '20131022'
)->returns( 15 ).

lo_is_in_time_info ?= lo_mocker->generate_mockup( ).
"will return lv_delay = 30
lv_delay = lo_is_in_time_info->get_delay(
iv_carrid = 'LH' iv_connid = '123' iv_fldate = '20131022' ).
"will return lv_delay = 15
lv_delay = lo_is_in_time_info->get_delay(
iv_carrid = 'LH' iv_connid = '123' iv_fldate = '20131022' ).

Mocking methods with exporting parameters

EXPORTING parameters can be returned by using the EXPORTS-Method
lo_mocker = zcl_mocka_mocker=>zif_mocka_mocker~mock(
'zif_mocka_is_in_time_info' ).
lo_mocker->method( 'GET_BOTH' )->with(
i_p1 = 'LH' i_p2 = '123' i_p3 = '20131023'
)->exports( i_p1 = 2 i_p2 = abap_true ).
lo_is_in_time_info ?= lo_mocker->generate_mockup( ).

"will return lv_delay = 2, lv_is_in_time = 'X'
lo_is_in_time_info->get_both(
EXPORTING
iv_carrid = 'LH'
iv_connid = '123'
iv_fldate = '20131023'
IMPORTING
ev_delay = lv_delay
ev_is_in_time = lv_is_in_time
).

Mocking methods with changing parameters

CHANGING parameters can serve both as input and output:
lo_mocker = zcl_mocka_mocker=>zif_mocka_mocker~mock( 'zif_mocka_is_in_time_info' ).

lo_mocker->method( 'IS_IN_TIME_BY_CHANGING_PARAM' )->with(
i_p1 = 'LH' i_p2 = '123' )->with_changing( i_p1 = lv_fldate
)->changes( '20131025' )->exports( i_p1 = abap_true ).

lo_is_in_time_info ?= lo_mocker->generate_mockup( ).

"will return lv_fldate = '20131025', lv_is_in_time = 'X'
lo_is_in_time_info->is_in_time_by_changing_param(
EXPORTING iv_carrid = 'LH' iv_connid = '123'
IMPORTING ev_is_in_time = lv_is_in_time
CHANGING cv_fldate = lv_fldate ).

Raise exceptions

Often unit tests also test unusual situations which are handled by raising and catching an exception. The mocker allows you to register a to-be-raised exception by using the methods RAISES( … ) or RAISES_BY_NAME( … )
lo_mocker = zcl_mocka_mocker=>zif_mocka_mocker~mock( 'zif_mocka_is_in_time_info' ).

lo_mocker->method( 'IS_IN_TIME' )->with(
i_p1 = 'LH' i_p2 = '123' i_p3 = '20131024' )->raises_by_name( 'zcx_mocka_in_time_exception' ).

lo_is_in_time_info ?= lo_mocker->generate_mockup( ).

TRY.
lo_is_in_time_info->is_in_time(
iv_carrid = 'LH' iv_connid = '123' iv_fldate = '20131024' ).
CATCH zcx_mocka_in_time_exception.
BREAK-POINT."program flow will halt here
CATCH cx_root.
BREAK-POINT."will not be called
ENDTRY.

Useful links

The Art of Unit Testing
Unit Tests in general

mockA released – a new ABAP Mocking Framework

Some good news…

The ABAP Mocking Framework presented last year is now Open Source.
mockA
The namespace has been changed to Z* in order to allow every interested developer to participate in the development of this tool.
Feel free to participate in the development process and visit the project page at Github.
Current Features

  • Mocking of ABAP interfaces and non-final classes
  • Conditional returning of RETURNING, EXPORTING and CHANGING parameters for methods based on specific parameter combinations of IMPORTING and CHANGING parameters
  • You can even define different output each time when the method is called multiple times with the same parameter values
  • raiseable exceptions
  • verification of mocked method call count

How to start

  • Visit us on Github
  • Check Out the daily build and import it to your SAP System using SAPLink
  • As there is no documentation yet, the Unit Test ZTEST_CL_MOCKA_MOCKER describes the features of the tool
  • The Demo report ZTEST_CL_MOCKA_FLIGHT_OBSERVER also demonstrates some features described last year

Functional gaps

  • Verification of mocked method calls against certain expected parameter input
  • Register pattern based method signatures for mocked methods

Credits

Thanks to leogistics GmbH, the formerly internal project is now open source. leogistics has decided to provide it as open source software to strengthen the SAP Netweaver and its ABAP development capabilities, and, of course to allow the community to benefit from this tool and make it even better.

Unit Tests in ABAP using a mocking framework

Introduction

Unit Tests are an integral part of the software development lifecycle. The purpose of tests is to ensure the proper functionality of a system. Also, if changes are made to specific parts of the system, Unit Tests can immediately show if any functionality has been broken during the last update.

A common problem in unit testing is to separate the system under test, which is subject to the unit test, from other parts which are not to be tested. Common parts, which need to be separated and replaced specifically for unit tests are usually business object repositories or database access components as well as components which rely on third party systems.

This blog is not about unit testing in ABAP. This blog is about developing them faster.

Scenario

For demo purposes, I build up a scenario. It includes a simple flight observer, which has access to a flight information system.

Furthermore, it has access to an alert component which raises mail, SMS etc. alerts.

Both components are dependencies on which the observer relies on. Both components will need to be replaced by mockups for testing purpose since they are not subject to the unit test. Rather, the flight observer is to be tested, in the following sections referred to as the system under test (SUT).

The SUT is implemented by /LEOS/CL_FLIGHT_OBSERVER.

The flight information component is described by the interface /LEOS/IF_IS_IN_TIME_INFO.

The alert processor is described by the interface /LEOS/IF_FLIGHT_ALERT_PROCESS.

UML

The validation logic is very simple: It decides, weather an alert for a specific flight is raised or not. Every delay with more than 60 minutes is getting raised. However, in any other real scenario, there wouldn’t be such a simple hard coded routine but for demo purposes it is fine.
All dependencies to the two other components are set via its constructor using dependency injection.

Unit Test realization Part I

The method which is going to be tested is described below and implements the 60-minutes rule: every delay of more than 60 minutes is getting delegated to the alert system.

This implementation is not a challenge at all. More tedious is the implementation of local classes which are implementing the functionality of the two components on which the SUT depends. These implementations are called mocks.

The flight information access, implementing /LEOS/IF_IS_IN_TIME_INFO, would have to return flights which are sometimes in time and sometimes not. This flight information would have to be hard coded in the unit test. It might look like this:

There would be another mockup for the alert component, tracking the number of calls made to the method ALERT_DELAY( ). We do not show it here.

The setup routine for the unit test to setup all the required objects would look like this:

The test now calls the SUT with various flight data. Some data will lead to a delay, like flight LH / 402 / in November 9th 2012, some will not (like LH / 402 in November 10th 2012). The test is going to evaluate if the alert processor has been called or not, depending on the requested flight data.

Unit Test realization Part II

The described approach requires  a lot of coding to implement the mockups. This coding is never ever reused and basically a waste of time.

This is where mocking frameworks come into place.

Mocking Frameworks are not meant for productive usage, however, their only usage is to save coding in unit tests, hence, development efforts.

The same mockup, which returns flight information data could be mocked using a mocking framework:

The whole mockup implementation is described by only two lines of ABAP coding. Three more lines include data declarations and one line actually generates the mock object for you. This sums up to 8 lines of code, in comparison to 14 lines of code which are needed for the manual implementation.

The mockup for the alert processor is even more simple, since its only purpose is to track the number of calls made against method ALERT_DELAY. This feature is already included in the mocking framework as described in the test coding utilizing these mocks. You would just need to call method HAS_METHOD_BEEN_CALLED( … ) on the corresponding mocker object.

Unit Test Coding

REPORT /leos/_test_flight_observer.

*----------------------------------------------------------------------*
* CLASS lcl_test_observer DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_test_observer DEFINITION FOR TESTING.
"#AU Risk_Level Harmless
"#AU Duration Short
PROTECTED SECTION.

DATA mo_is_in_time_access TYPE REF TO /leos/if_is_in_time_info .
DATA mo_is_in_time_mocker TYPE REF TO /leos/if_mocker.
DATA mo_alert_processor TYPE REF TO /leos/if_flight_alert_process .
DATA mo_alert_processor_mocker TYPE REF TO /leos/if_mocker.

DATA mo_system_under_test TYPE REF TO /leos/cl_flight_observer.

PRIVATE SECTION.
METHODS setup.
METHODS teardown.
METHODS test_no_alert FOR TESTING.
METHODS test_with_alert FOR TESTING.
ENDCLASS. "lcl_test_observer DEFINITION

*----------------------------------------------------------------------*
* CLASS lcl_test_observer IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_test_observer IMPLEMENTATION.
METHOD setup.
DATA lo_mocker_method TYPE REF TO /leos/if_mocker_method.
* create the flight information backend
mo_is_in_time_mocker = /leos/cl_mocker=>/leos/if_mocker~mock( iv_interface = '/LEOS/IF_IS_IN_TIME_INFO' ).
lo_mocker_method = mo_is_in_time_mocker->method( 'GET_DELAY' ).
lo_mocker_method->with( i_p1 = 'LH' i_p2 = 402 i_p3 = '20121109' )->returns( 100 ).
lo_mocker_method->with( i_p1 = 'LH' i_p2 = 402 i_p3 = '20121110' )->returns( 5 ).
* this call creates the flight information mockup
mo_is_in_time_access ?= mo_is_in_time_mocker->generate_mockup( ).

* create an empty alert backend (we just need to track the number of method calls)
mo_alert_processor_mocker = /leos/cl_mocker=>/leos/if_mocker~mock( iv_interface = '/LEOS/IF_FLIGHT_ALERT_PROCESS' ).
* this call creates the alert processor mockup
mo_alert_processor ?= mo_alert_processor_mocker->generate_mockup( ).

* create the flight observer which is subject to this test
CREATE OBJECT mo_system_under_test
EXPORTING
io_alert_processor = mo_alert_processor
io_in_time_access = mo_is_in_time_access.
ENDMETHOD. "setup
METHOD teardown.
ENDMETHOD. "teardown
METHOD test_no_alert.
mo_system_under_test->observe_flight( iv_carrid = 'LH' iv_connid = 402 iv_fldate = '20121110' ).
cl_aunit_assert=>assert_initial( mo_alert_processor_mocker->has_method_been_called( 'ALERT_DELAY' ) ).
ENDMETHOD. "test_no_alert
METHOD test_with_alert.
mo_system_under_test->observe_flight( iv_carrid = 'LH' iv_connid = 402 iv_fldate = '20121109' ).
cl_aunit_assert=>assert_not_initial( mo_alert_processor_mocker->has_method_been_called( 'ALERT_DELAY' ) ).
ENDMETHOD. "test_with_alert
ENDCLASS. "lcl_test_observer IMPLEMENTATION

Conclusion

This was only a simple example. But even this example showed how easy about 40% of code required to mock objects could be saved. What about real world scenarios with even more complex dependencies and behaviour? In fact, 40% of coding can be saved also in these projects, often even more. Most objects can be easily mocked using a mocking framework if they are really treated as a black box.

What a good mocking framework should also provide, is mocking of specific classes using inheritance. Also, exceptions might need to be registered for specific inputs.

Currently, the described mocking framework is still under development. It supports mocks based Interfaces, method call observation and raising of exceptions but not yet the mocking based on already existing classes. However, it is in productive usage in our projects and has already been extremely valuable for making unit tests simpler and easier to implement.

Further blogs may focus on how to manage dependencies in big development projects and how dependency injection might work in ABAP using an IoC Container. Stay tuned.