Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
ennowulff
Active Contributor
One year ago, on June 10th 2017, Stefan Schmöcker and I presented GLADIUS on SAP Inside Track in Hamburg.

GLADIUS


The idea was to create a coding challenge platform like https://codewars.com or https://codefights.com. Only with ABAP. This idea turned out to be quite complex. That's the reason why Stefan and I didn't work any further on this project.

We now sat together to starting the next approach on GLADIUS. This is the first blog of hopefully many more about code challenges, units testing and learning.

Why GLADIUS?


Gladius is the name of the famous roman sword. Coding and fighting has a lot of similiarities: you have to work hard to become a good programmer, you have to practice, you need good tools. That's why we thought that GLADIUS might be a good and appreciable name for a learning tool.

What is it all about?


Codewars or Codefights challenges you to write a function that returns the right result for a given input. You can code in many different languages like C++, Javascript, Ruby, php and others.

The function can be faulty so that you have to correct it or it can be completely empty so that you will have to code the complete function to return the correct result.

Unit tests


Your result will be checked by plenty of given unit tests. If there are wrong tests you must correct the function. If all tests are passed, you won the challenge.

There are visible and hidden tests. For visible tests you can see the input and the result. Hidden tests only tell you if it passed or not.

How do I know the right results?


There are mainly two types of challenge:

a) You will be given a detailed description of the problem. You will get examples and a lot of hints what the function should do and what not.

b) You only have unit test. You must derive the functionality by the results given in the unit tests. My favourite is this one:

1 = pump
5 = pump
10 = feet
12 = stomping

What are the results for 2, 3, 4...? 🙂

Back to ABAP


With this blog series I will try to tell you step by step what we are planning and what are the basics. At the end we dream of a magic workbench where complete workshops or courses can be defined, & tested, where you can create statistics and define or measure metrics and many more.

The following steps will show you the most simple framework to starting something like ABAP code fights.

The ingredients


All of the following objects are fully integrated in the ABAP workbench or ABAP in Eclipse. We will use the integrated ABAP Test Cockpit functionality.

These are the objects we need:

  • Interface which defines the method (importing and returning parameters)

  • A helper class to access different solution classes

  • A master class which hold the complete solution of the functionality

  • A class with all test units


The Interface


Define an interface with a simple method TEST_ME and the importing parameter IN and the returning parameter OUT.
INTERFACE zif_glds_demo_test
PUBLIC .

METHODS test_me
IMPORTING
!in TYPE i
RETURNING
VALUE(out) TYPE i .
ENDINTERFACE.

This interface will be embedded in all solution classes that someone would like to create.

The Helper


The target is to have one class with test units and many solution classes of different people. But we do not want to implement unit tests to every solution class. We will create one super test unit class, the test master, and derive from this class.

To have access to the very class of the particular solution, we need a trick which we will implement in the helper class:
CLASS zcl_glds_demo_test_helper DEFINITION
PUBLIC
ABSTRACT
CREATE PUBLIC .

PUBLIC SECTION.

DATA mo_class_to_test_generic TYPE REF TO object .

METHODS constructor .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.



CLASS zcl_glds_demo_test_helper IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_GLDS_DEMO_TEST_HELPER->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD constructor.

TRY.
DATA(lv_classname) = cl_abap_classdescr=>get_class_name( me ).
FIND REGEX '\\PROGRAM=([^=]+)' IN lv_classname SUBMATCHES lv_classname.
lv_classname = lv_classname(30).
CREATE OBJECT me->mo_class_to_test_generic TYPE (lv_classname).
CATCH cx_root.
ENDTRY.

IF me->mo_class_to_test_generic IS NOT BOUND.
MESSAGE a000(oo) WITH 'unable to create class to test' lv_classname.
ENDIF.

ENDMETHOD.
ENDCLASS.

The class has the generic attribute MO_CLASS_TO_TEST_GENERIC. In the CONSTRUCTOR we will match the respective class to the test object.

The Test Units


Now that we have the helper class, we can code the test units. This test unit class derives from the helper class.

It is important that this class is abstract and that it is a test unit class!



The test methods (= unit tests) must be public and have the flag "testmethod" activated:


CLASS zcl_glds_demo_test_units DEFINITION
PUBLIC
INHERITING FROM zcl_glds_demo_test_helper
ABSTRACT
CREATE PUBLIC
FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS .

PUBLIC SECTION.

METHODS constructor .
METHODS test_0 FOR TESTING .
METHODS test_1 FOR TESTING .
METHODS test_2 FOR TESTING .
METHODS test_3 FOR TESTING .
METHODS test_10 FOR TESTING .
METHODS test_99 FOR TESTING .
METHODS test_random FOR TESTING .
PROTECTED SECTION.
PRIVATE SECTION.
DATA mo_class_to_test TYPE REF TO zif_glds_demo_test .
ENDCLASS.



CLASS zcl_glds_demo_test_units IMPLEMENTATION.

METHOD constructor.
super->constructor( ).
mo_class_to_test ?= mo_class_to_test_generic.
ENDMETHOD.

METHOD test_0.
cl_abap_unit_assert=>assert_equals(
exp = 0
act = mo_class_to_test->test_me( 0 )
msg = '0 should be 0' ).
ENDMETHOD.

METHOD test_1.
cl_abap_unit_assert=>assert_equals(
exp = 0
act = mo_class_to_test->test_me( 1 )
msg = '1 should be 0' ).
ENDMETHOD.

METHOD test_10.
cl_abap_unit_assert=>assert_equals(
exp = 90
act = mo_class_to_test->test_me( 10 )
msg = '10 should be 90' ).
ENDMETHOD.

METHOD test_2.
cl_abap_unit_assert=>assert_equals(
exp = 2
act = mo_class_to_test->test_me( 2 )
msg = '2 should be 2' ).
ENDMETHOD.

METHOD test_3.
cl_abap_unit_assert=>assert_equals(
exp = 6
act = mo_class_to_test->test_me( 3 )
msg = '3 should be 6' ).
ENDMETHOD.

METHOD test_99.
cl_abap_unit_assert=>assert_equals(
exp = 9702
act = mo_class_to_test->test_me( 99 )
msg = '99 should be 9702' ).
ENDMETHOD.

METHOD test_random.
DATA(rnd) = cl_abap_random_int=>create( seed = 42 min = 10 max = 10000 )->get_next( ).
DATA(result) = rnd * ( rnd - 1 ).
cl_abap_unit_assert=>assert_equals(
exp = result
act = mo_class_to_test->test_me( rnd )
msg = |{ rnd } should be { result }| ).
ENDMETHOD.
ENDCLASS.

Make sure that the MO_CLASS_TO_TEST attribute referrs to the test interface.

The class to test inside the test units must be the MO_CLASS_TO_TEST attribute!

The Test Master


The test master class only implements the interface to have the structure of the test method and provides the correct functionality.
CLASS zcl_glds_demo_test_master DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .

PUBLIC SECTION.

INTERFACES zif_glds_demo_test .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.

CLASS zcl_glds_demo_test_master IMPLEMENTATION.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_GLDS_DEMO_TEST_MASTER->ZIF_GLDS_DEMO_TEST~TEST_ME
* +-------------------------------------------------------------------------------------------------+
* | [--->] IN TYPE I
* | [<-()] OUT TYPE I
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD zif_glds_demo_test~test_me.

IF in = 0.
out = 0.
RETURN.
ENDIF.

out = in * ( in - 1 ).

ENDMETHOD.
ENDCLASS.

The function simply multiplies the given value by itself minus one.

Note that this class has local test units! But these test units are not implemented in this class but are derived from the global test units class.
CLASS lcl_test DEFINITION
INHERITING FROM zcl_glds_demo_test_units
FOR TESTING
RISK LEVEL HARMLESS
DURATION SHORT.

ENDCLASS.

Ready Player 1


This is the most simple framework to start implementing your own function:

Of course someone who wants to implement the function should not see the code of the master solution! You can provide a class which implements the interface and derives from the global test class.

Simply create a class like ZCL_GLDS_DEMO_SOLUTION_1, implement the interface ZIF_GLD_DEMO_TEST and create a local test unit section like the one above.

After activating all that stuff and running the unit tests via CTRL-SHIFT-F10 you will see that there are errors.


Next...


Maybe you can imagine that there is a lot of potential in this...

At least the administration design should look something like this:



Administrators can define different projects for workshops with different tasks. These can be tasks to new ABAP features, mathematical problems or customer specific functions.

Users can register for a project. Team leaders can see their achievements in overviews.

Test classes might be generated automatically.

The source code might be stored as a string in the database instead of a global class in the repository.

There might be challenges: Who provides the fastest solution to a task? Who uses the least number of statements?

Book authors might enrich their examples with GLADIUS test cases so that learners can interactively practice the books theory.

We could also think of a defined function (test interface) and the administrator simply enters the input values and the expexted results in the application. No need for programmed test class. This could bring test driven development to a complete new level. Imagine that your key users "write" the test cases... 😉

abapGit


All sources also on github: https://github.com/tricktresor/gladius

Tribute to lars.hvam

See the 2nd part here: GLADIUS - The Next Level
12 Comments