News about mockA

The current release of mockA is available at Github. It contains an error fix that I would like to outline in today´s blog post.

 

The bug

MockA allows you to mock classes as described in one of my previous blog posts. Technically, mockA tries to create a subclass of the class which is subject to the mock creation. This means, it will only work, if the class is not marked as final and has a constructor which is at least protected or public.

MockA overrides methods that should be mocked, with a local implementation that returns the values expected to be returned. It follows the specifications set up by the unit test, according to the method( ), with( ) and exports( ) or returns( ) -calls (and so on) during mock creation.

 

There is another feature that reuses generated subroutine pools that have been created by mockA, because the Web Application Server ABAP allows only about 36 subroutine pools for each program, or, in our case, per unit test. The generated code does not contain any hard coded method output parameters as there would be no benefit in buffering the generated coding then. Instead, the instance of type ZIF_MOCKA_MOCKER is passed to the mock object. In the mock object´s method implementations, the fake values are read from that instance. If a new mock object should be created, a new instance of ZIF_MOCKA_MOCKER will be passed to the mock object. Hence, the method output may change.

 

If mockA generates the local implementation of an interface, each method is implemented during the initial mock class generation, so this feature poses no issues here.

However, in case a class needs to be mocked, mockA also tried to reuse these generated subroutine pools in the past. Do you see the error?

 

What could possibly happen

Imagine, mockA should create a mock implementation of the following class: ZCL_I_CAUSE_TROUBLE which has two methods:

  • METHOD_A
  • METHOD_B

  In our first unit test, we will tell mockA to simulate the output of METHOD_A, without defining any output for METHOD_B. 

When the mock object is created, mockA will generate a local subclass of ZCL_I_WILL_CAUSE_TROUBLE, with a local implementation of method METHOD_A that overrides the parent´s class method. The parent´s class method cannot be called any longer via the mock object. METHOD_B_ remains untouched.

After generation of the subroutine pool, the class implementation is buffered for later usage.

 

If another unit test, that is executed after the first one, wants to control the output of METHOD_B, mockA won´t return that output, as the method has not been overridden in the first unit test and therefore no output control takes place in the locally created class implementation: The logic that is responsible for returning the specified fake values is simply not called. Instead, the super implementation of ZCL_I_WILL_CAUSE_TROUBLE is called.

 

The solution

Subroutine pool buffering is now generally switched off if a class needs to be simulated. For interfaces, the current logic remains unchanged.

Unfortunately, this change is a breaking change, which can lead to failing unit tests, which have passed in the past.

This change could cause some new issues that I would like to outline briefly:

  1. Subroutine pool limits might be violated for existing unit tests. As there might be multiple implementations generated per class within the same unit test report, the subroutine pool limit might be violated once you updated mockA. In this case, please split up your test methods into various reports, if possible.
  2. Please see the example above: If your second unit test tells mockA to simulate the output of METHOD_B, but actually expects a result that is returned by the super implementation, your unit test might fail now, as the super implementation is not called any longer due to the correction and instead, the specified fake values will be returned.
    I know that this is just a theoretical consideration but important to be mentioned. Nevertheless, these test cases can be considered incorrectly implemented, as the unit test possibly expects other values than the values that have been defined as output for METHOD_B. Hence, these test cases should be reviewed anyway!

 

Feedback welcomed

There is no possibility to switch off the currently implemented behaviour of mockA as I think it is more important to fix the error than to allow old and incorrect unit test implementations not to fail.

 

Please let me know, if you run into trouble with the new update, and if issue 1) or 2) applies, or maybe both. Please also tell me, if you figured out another issue that I didn´t think of now.

Advertisements

mockA – Carry out checks against parameters passed to the mock object’s methods

What has been missing so far

In one of my last blog posts, I showed you how mock objects can be easily created. I also showed you how mock object’s method calls can be counted and how verifications can be implemented, to ensure that a mocked object’s method has been called.
Up to now, it was not possible to check, which parameters have been passed.

Ensure, that a mocked object has been called with certain parameters

In the current build, I also included the possibility to verify the parameters that have been passed to a mock object’s method.
This is how it works:
First of all, you need to keep track of the mocker object of type ZIF_MOCKA_MOCKER (which means, please do not throw it away 😉 )
By utilizing this mocker object, you can get data about your mocked methods, such as if it has been called with certain parameters or not:

DATA lv_has_been_called TYPE abap_bool.
lv_has_been_called = lo_mocker->method( 'get_delay' )->has_been_called_with(
i_p1 = 'LH'
i_p2 = '300'
i_p3 = sy-datlo
).

For further details, please see report ZMOCKA_DEMO that is shipped with the current release.

Limitations

Only IMPORTING parameters are supported so far, this means no CHANGING parameters can be verified yet.

Mock specific classes with mockA

With mockA it is quite easy to mock interfaces for unit tests. But it is also capable of creating mock objects that are not based only on interfaces, but specific classes. This blog post shows how it works and what needs to be considered.

Basics

Creating such an instance is quite the same as the creation of mock objects based on interfaces. The only restriction that applies is the fact that the class which is to be mocked may not be final class. This is necessary because mockA couldn’t create a subclass to override method outputs.

The classes in the following examples can be found in the mockA package provided at Github.

DATA lo_mocker TYPE REF TO zif_mocka_mocker.
DATA lo_mocker_method TYPE REF TO zif_mocka_mocker_method.
DATA lo_flight_observer TYPE REF TO zcl_mocka_flight_observer.
lo_mocker = zcl_mocka_mocker=>zif_mocka_mocker~mock( zcl_mocka_flight_observer=>gc_name ).
lo_mocker_method = lo_mocker->method( 'observe_flight' ).
lo_mocker_method->with( i_p1 = 'NA' i_p2 = 007 i_p3 = sy-datlo ).
lo_mocker_method->returns( abap_true ).
lo_flight_observer ?= lo_mocker->generate_mockup( ).

Constructor parameters

However, this little example will still fail, as ZCL_MOCKA_FLIGHT_OBSERVER’s constructor expects non-optional IMPORTING parameters. This is an issue which is not existent for interfaces. As you mock already existing implementations, you also need to take care of that.

But that is also no problem at all. Consider the following example that passes some earlier created instances to the constructor. It can be achieved by calling the method PASS_TO_SUPER_CONSTRUCTOR of the mocker instance.
DATA lo_is_in_time_info TYPE REF TO zif_mocka_is_in_time_info.
DATA lo_flight_alert_process TYPE REF TO zif_mocka_flight_alert_process.
*create lo_is_in_time_info and lo_flight_alert_process... (not shown here)
DATA lo_mocker TYPE REF TO zif_mocka_mocker.
DATA lo_mocker_method TYPE REF TO zif_mocka_mocker_method.
DATA lo_flight_observer TYPE REF TO zcl_mocka_flight_observer.
lo_mocker = zcl_mocka_mocker=>zif_mocka_mocker~mock( zcl_mocka_flight_observer=>gc_name ).

lo_mocker->pass_to_super_constructor(
i_p1 = lo_flight_alert_process i_p2 = lo_is_in_time_info ).
*mock some method output (not shown here)
lo_flight_observer ?= lo_mocker->generate_mockup( ).

The example s are also in the mockA package. Take a look at the unit test report ZTEST_CL_MOCKA_MOCKER and the test methods mock_class_with_construc_param, mock_class_with_method_output and mock_intf_with_construc_param.