Introduction

This blog post is strongly connected to the presentation that has been given by Damir Majer and Martin Steinberg during SAP Inside Track 2014 in Munich.

The presentation focuses on solving a code kata using Test-Driven Development (TDD).

The purpose is to show how TDD can lead to better tests coverage, thus more robust and maintainable software components. This blog post focuses not about the solution of the code kata itself, but rather on how to test each component separately from each other using mockA.

The Kata

Summarized, the Kata says:

Implement a simple String calculator class that accepts a flexible amount of numbers in a string. The numbers should be summed up and the sum needs to be returned.

Examples:

  • An empty string returns “0”
  • For single numbers, the number itself will be returned, e.g. for “1”, the sum 1 will be returned
  • For “1,2”, the sum 3 will be returned
  • Also multiple delimiters will have to be accepted, e.g. “1;2\3;1” will lead to 7
  • This also applies to line breaks like “\n”. “1\n2,3” results in 6
  • Delimiters might have variable length. “//***\1***2***3\2***2” results in 10
  • Raise an exception in case negative numbers are passed to the method
  • Numbers bigger than 1000 should be ignored

The Kata requires you to implement the code step by step, without skipping steps. Every step should contain

  • A unit test that tests the requirement and will fail at the first run
  • An implementation that covers the requirement
  • A new unit test run that will succeed
  • Refactoring
  • Running the test again to ensure nothing broke

The Solution

The solution class can be found in the attachments (“zcl_string_calculator.txt”).

The class ZCL_STRING_CALCULATOR contains

  • One protected method that replaces all delimiters with a comma (“replace_delimiter_with_comma”)
  • One protected method that sums up the consolidated string (“compute”)
  • One public method to rule them all (“add”)
  • Several attributes

“add” basically delegates the task of replacing all delimiters with commas to a specific protected method. It uses its output to sum up the values.

The Unit Test report “Unit Test v1.txt” shows the corresponding unit tests.

Isolate helper methods from the add-method

While “replace_delimiter_with_comma” and “compute” are helper methods, the public “add”-method delegates its own calls to these methods. Thus, it is dependent from the helper methods.

In some point of time, it might be helpful to check, if the “add”-method works as expected, which means, that it delegates its calls correctly to the helper method.

Think of the following unit test, which does not directly link to the code kata, but may ensure code quality:

  • Test the “add” method with the string “<1sd2,3rtrt,4”
  • Ensure, that “add” calls “replace_delimiter_with_comma” with “<1sd2,3rtrt,4”
  • The call will return “1,2,3,4”
  • Ensure, that “compute” will be called with “1,2,3,4”
  • Ensure, that the output of compute is returned without modification (result will be 10)

Such a test will need you to subclass ZCL_STRING_CALCULATOR and redefine the helper methods with hard coded return values based on specific inputs. Furthermore, some logic behind “compute” should allow you verify if the method has been called with the correct parameters.

Subject to the test will be the subclass of ZCL_STRING_CALCULATOR, which will partly contain so called faked functionality regarding “replace_delimiter_with_comma”. But it will also contain some mock features, as “compute” should not only conditionally return some values based on its input, but it should also allow you to determine, if it has been called with the expected input.

mockA allows you to skip this subclassing and lets you focus on the test. It will create a subclass at runtime for you, which follows constraints like conditional method output. These constraints can be hard coded by you. It will also allow you to verify method calls of mocked methods.

“Unit Test v2.txt” shows you how to do it. Keep a look a test method “test_add”.

The first call


    lo_mocker = zcl_mocka_mocker=>zif_mocka_mocker~mock( 'ZCL_STRING_CALCULATOR' ).
    lo_mocker->method( 'replace_delimiter_with_comma'
      )->with_changing( '<1sd2,3rtrt,4'
      )->changes( '1,2,3,4'
    ).
tells mockA to fake the method “replace_delimiter_with_comma”, while
    lo_mocker->method( 'compute'
      )->with( '1,2,3,4'
      )->returns( 10
    ).

tells mockA to fake the output of “compute”.

Subject to the test will be the object generated by mockA (which is a subclass of ZCL_STRING_CALCULATOR in reality)

    go_string_calculator ?= lo_mocker->generate_mockup( ).

After the call of “add”, the result is verified in the unit test. But besides this verification, you may also ensure, that “compute” has been called correctly with the input value “1,2,3,4”:


    DATA lv_has_been_called_correctly TYPE abap_bool.
    lv_has_been_called_correctly = lo_mocker->method( 'compute' )->has_been_called_with( '1,2,3,4' ).
    assert_not_initial( lv_has_been_called_correctly ).

Further information

You may find further information of the presenters at

damir-majer.com / @majcon

http://about.me/martin.steinberg / @SbgMartin

attachments: / – uwekunath-wordpress-com – attachments for blog posts – Google Project Hosting

To report this post you need to login first.

8 Comments

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

  1. Gareth Ryan

    Hi,

    As per forum guidelines, could you all please stick to English so everyone can try and read what you are posting – as it is, I have no idea what you are talking about but suspect it could be quite interesting!

    Thanks,

    G.

    (0) 
    1. Sven Schiffmann

      Hi Gareth,

      sorry, I repeat in English, this was my first post 😉

      I had a problem with a Mock-method without parameters that should return different results (true/false). Because a class once mocked will be reused I’m only able to mock this method with result either true or false.

      Reason for this behaviour is that the mocked class is generated as a subroutine pool and limited to 36. I asked to add option to disable the reuse so that I’m able to create 2 different mocks for the method. In one mock it should return true, in the other mock false.

      (0) 
      1. Uwe Kunath Post author

        Hi,

        what about the following possibility:

        Instance buffering is generally disabled when mocking classes. Buffering of class implementations in combination with overriding only the methods that have been mocked BEFORE the first generation is a clear error that must be fixed.

        If I change that behaviour, I might break some test cases, as some test cases might utilize a mock instance created out of a reused class whose method has been mocked in the second test case, but actually calls the super implementation, because this method has not been mocked before the first mock generation.

        I consider these test cases extremly rare, and, if they exist, should be reviewed anyway as they rely on super implementations that were originally intended to be mocked.

        Regards,

        Uwe

        (0) 
      2. Uwe Kunath Post author

        Dear Sven,

        please check if the following coding in class ZCL_MOCKA_MOCKER, method ZIF_MOCKA_MOCKER~GENERATE_MOCKUP brings the desired result:

        Replace starting by line 272 with:

           CONCATENATE `\PROGRAM=` lv_prog `\CLASS=` lv_classname INTO mv_generated_class.

           ro_mockup ?= try_create_by_generated_class( ).

           DATA ls_generated_class TYPE ty_s_generated_class.

           IF mv_is_interface_mock = abap_true.

             ls_generated_classname = mv_interface.

             ls_generated_classtechnical_name = mv_generated_class.

             APPEND ls_generated_class TO mt_generated_classes.

           ENDIF.

        ENDMETHOD.                    “ZIF_MOCKA_MOCKER~generate_mockup


        Thank you,


        Uwe

        (0) 
  2. Sven Schiffmann

    Dear Uwe,

    I tried and it worked because in my case I mock a non-final class.

    But what about if someone has an interface with a method without parameters? In this case the mock will be buffered and it’s again not possible to implement the method with different returning values.

    In my example I mock a method to check if a record with empty WERKS (default record) exists in a customizing table. For different testcases I need this method to return true or false.

    Because I have no interfaces (yet) this solution is fine. But probably others use interfaces and have a similar request.

    Regards,

    Sven

    (0) 
    1. Uwe Kunath Post author

      Dear Sven,

      thank you for testing. I’ve updated mockA and wrote a blog post News about mockA

      To answer your question briefly: Interface implementations never contain return hard coded values, so the shoul be reusable.

      Regards,

      Uwe

      (0) 

Leave a Reply