ABAP Unit - Structure of test cases
In this article we will take a look at how a test case in ABAP Unit should be structured in order to be easy to read and still is well structured.
In this article we want to go into more detail on the structure and design of the test cases. These also follow some standards and help other developers to understand your test cases quickly and easily.
Test methods should always follow the pattern “Given - When - Then” so that it is immediately apparent what is being tested and against what the result is being validated. To do this, the test method from the last chapter again:
The individual sections can be described as follows:
- Given - Preparation of the test instance and data required for the test case
- When – Execution of the test or calling up the test method
- Then - Comparison of the result against the expected value (use of class CL_ABAP_UNIT_ASSERT)
You can also use auxiliary methods in the test class or another auxiliary class to prepare the data and test instance. Above all, clean code plays an important role here, so that nothing is redundant in test code. Everything that can be outsourced during preparation should be fulfilled by other methods so that your test method covers the pure test case and remains clear.
The definition of the test method returns at least the class to be tested as a completed instance.
METHODS: prepare_cut IMPORTING id_companycode TYPE t001-bukrs DEFAULT '' RETURNING VALUE(ro_cut) TYPE REF TO zcl_test_prepare.
In the method, the initial configuration can then be carried out accordingly in order to initialize all values for the test cases.
METHOD prepare_cut. DATA(ld_companycode) = id_companycode. IF ld_companycode IS INITIAL. ld_companycode = '4711'. ENDIF. DATA(ls_config) = VALUE zcl_test_prepare=>ts_config( test = abap_true send_mail = abap_false ). ro_cut = NEW #( ls_config ). ro_cut->set_company_code( ld_companycode ). ENDMETHOD.
During execution, the method to be tested is called and the interface is supplied with appropriate values. The result is best saved in a variable called “Result”. At this point it is also clear to other developers that this is the result of the test.
In most cases it is sufficient to compare against the method ASSERT_EQUALS, but it does not always make sense to do this or the result is simply not available. Here you should check from time to time whether another method is also possible or if you want to achieve an approximation.
Here are a few examples:
- Status of a table access or a table action - ASSERT_SUBRC
- Filling the data - ASSERT_NOT_INITIAL
- Instance was created - ASSERT_BOUND
Code under Test
As you have probably seen in some of our test methods, the instance of the class we are always testing is called “lo_cut”. There is also a simple reason for this, because there is one other thing that you should be aware of and that is that the Code under Test (CuT for short) is also named in the test case to make it clear to other developers what exactly is being tested.
If more than one class is instantiated in the test case, this is the clear indication of the test case and which object is then tested. The instance can also be defined as an attribute in the test class, but then it is also given this name.
A corresponding test method for the code snippets mentioned above could then look like this. You can easily identify the class to be tested:
METHOD company_data_not_found. DATA(lo_cut) = prepare_cut( ). DATA(ls_found) = lo_cut->get_company_data( ). cl_abap_unit_assert=>assert_initial( ls_found ). ENDMETHOD.
How many test cases do you actually need for your coding? There is no precise rule for this, but you have to think about it here. In the first step, you should definitely check your technical requirements for the code, i.e. what the test object should actually do:
- Positive test - you call up the test object with valid values and check whether the expected result has occurred and the object has behaved correctly.
- Negative test - you pass incorrect parameters and thus test the exception handling and behavior.
- Random - Pass in any random input to test the behavior of your code for stability.
- Overflow Test - You are passing fields that are too large or unusual data in order to push the internal data types and sizes to the limit. This is a severe stress test for the test object.
The further down you get in the list, the less often these types of testing methods are needed. How much you want to stress a method depends in the end on the time you want to test your methods.
Hint: If errors occur in certain methods in the course of the software life cycle, you can create test cases for these errors so that they no longer appear in the future. This helps you to keep the manual test scope as small as possible.
You should now be a little clearer about how a test case is structured in order to achieve the optimal benefit and to be as short as possible. Furthermore, you can now determine the scope of your tests yourself in order to determine the optimal test cases.