Isolate components for better testing with mockA
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.
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.
- 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
- Running the test again to ensure nothing broke
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 ).
You may find further information of the presenters at
damir-majer.com / @majcon
http://about.me/martin.steinberg / @SbgMartin