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.