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: 
adam_krawczyk1
Contributor

Hi,

I believe that writing unit tests is very important to have reliable and good quality software. I heard many times from other developers that customers are not interested in unit tests as they do not want to pay for that, but do they want to pay for even higher maintenance costs? Unit tests are not additional feature, they are part of good clean code that works. In this blog I want to present you how simple it is to create basic unit tests and what is the difference between SE80 and eclipse in this area. Even if you have already learned how to create unit tests in SE80, it may be difficult to start again in eclipse.

This article has few sections:

  1. Why we need unit tests
  2. Where to use unit tests
  3. Unit test class format
  4. Predefined unit test methods
  5. How to build single unit test case
  6. Unit tests in SE80
  7. Unit tests in eclipse
  8. Executing unit tests
  9. Summary

1. Why we need unit tests?

- They verify if code behavior is correct.

- They run fast and give quick status feedback.

- They lead to more reliable code, main bugs are found earlier.

- They lead to better and simpler code design.

- They help to consider all possible input/output values.

- They can be automated.

- In long term they reduce maintenance costs much.

Having just few unit tests is not a lot, but it is a good start. If every developer starts to create unit tests regularly, there will be small islands of tested code which finally will lead to larger, well tested areas.

2. Where to use unit tests?

Unit tests works easiest with classes. They are built inside class and are integrated part of object oriented design. However it is possible to write unit tests for function modules as well. Block of code (method, function, form) is easy to test if it is isolated - works only on input and output parameters and does not use external or global variables. That is why it becomes more difficult to test old legacy code and much easier to test new development - active usage of unit tests leads to better code design.

In general unit tests, as name suggests, are designed for limited scope, basic unit behavior testing. It is easy to test smallest methods but not complex logic of report. If we are not able to test full report flow, then try to test small parts of it, so we have solution tested on components level but not as a whole. For example lets have one method run_program_logic and 10 sub-methods inside. If we test all 10 sub-methods without testing main method, probably main flow logic will still work correctly, at least we will not experience problems with basic things like calculations, data conversion or format display. End to end testing must be anyhow done in user acceptance tests, not as unit tests. Testing full report in unit tests requires more time because we need to simulate many database queries etc. but at least we can try to test main business logic.

We cannot test directly blocks of report like INITIALIZATION, but we can implement these with object methods and then test them. For example:

INITIALIZATION.
lo_report->initialize_screen_fields( ).

For such code we can write unit tests for lo_report->initialize_screen_fields( ). Modularization is important.

3. Unit test class format

Unit tests are built in a class (local or global) with specific additions in definition part:

class ltcl_my_test_class

definition for testing

duration short

risk level harmless

.

Duration and risk level are test class attributes. Duration describes how long system accepts test run before termination, risk level allows to disable test execution in case of high risk. Good unit tests should have default values as above - run quickly with no harm to the system.

Usually global class will have local class for own code testing, but it is possible to use global test class if we want to share test code for more objects and reuse it outside class.

4. Predefined unit test methods

There are some method names that are already reserved and if we implement them, they will be automatically called by unit tests framework:

- class_setup - class method called once before all tests are run. Place for general initialization for static variables.

- class_teardown - class method called once after all tests are executed.

- setup - method called before each single test, commonly used for data preparation.

- teardown - method called after each single test.

All these methods are optional. I recommend to always use at least setup method to create new object for testing. Each single test case should perform steps on clear instance.

5. How to build single unit test case.

Single unit test is implemented as method in unit test class. We can identify 3 phases of test:

1. Initialization.

2. Code execution.

3. Results validation.

First two are nothing new - we need to write some code that will prepare data and run production code that we want to test, for example single method of a class. Third phase however requires additional, special methods to be called that will validate if results are correct. We call them assertion methods.

There are different types of assertions, they can be found as static methods in standard class cl_abap_unit_assert. Most popular assertions are:

- assert_equals - check if values are same,

- assert_initial - check if value is initial,

- assert_true - check if condition is true.

The goal of assertion method is to compare actual and expected value and raise error to unit test framework in case of not matching results. Important parameters are:

- act - what is the actual value retrieved from code execution (phase 2).

- exp - what is the value that we expect. Some assertions does not need it, like assert_initial or assert_true - only act value is needed.

- msg - what should be the message shown to user in case if test fails. I recommend to use it often although it is optional parameter - help others to understand what is the meaning of test case.

Example of method assertion:

    cl_Abap_Unit_Assert=>assert_Equals(
      act   = lo_calculator->add(
        i_num1 = 5
        i_num2 = 10 )
      exp   = 15         
      msg   = '5 + 10 must be 15'
    ).

Example above shows case when all three test phases are written as one statement: initialization (i_num1 = 5 and i_num2 = 10), execution (lo_calculator->add) and verification (assert_equals). This flexible call is possible with object oriented approach and I recommend to use it as it saves space.

In general each test method should perform single scenario validation, so it is good to separate methods for different variants. As example consider test methods like:

METHODS divide_success.

METHODS divide_div_zero_exception.

METHODS divide_missing_params.

It is better to have 3 methods instead of one that tests all 3 cases inside. Why? If test case fails, we know from name what exactly stopped to work (let say only divide_div_zero_exception failed). In this case name of test is description not only which method was tested, but also which case of that method was run. It brings value especially if we have automated tests scheduled periodically and general overview of all tests. Of course it is also acceptable to have only one test method "divide" and test different scenarios inside - we will still see that "divide" method fails. Sometimes it is easier to have more scenarios in one test method as we do not need to copy paste same code, however in general it is better to split scenarios to different test methods and assign meaningful name.

6. Unit tests in SE80

It is easy to create unit tests for global class in SE80. There is a nice wizard (Utilities -> Test Classes -> Generate), that helps us to create unit tests for chosen methods of a class:

In the wizard we can also decide which predefined methods we want to have and what are attributes of class.

- Fixture creates setup and teardown methods.

- Class fixture creates class_setup and class_teardown methods.

- Invocation creates method execution and default initial parameters assigned.

- Default Assert Equals syntax may be also created.

I recommend to select all options to have full test class generated so we can remove later some parts if we do not need.

After we go through wizard, local class with unit tests is automatically generated. If we selected "Generate Fixture" option, setup method will be created together with f_cut object which means class under test (f_ prefix although convention for object is mo_). And we see that new object instance is created in setup, so each test will have fresh instance to test.

Now the only thing we need to do is to update test methods content with test case scenario. In addition from source code editor we can add new test cases to the class if we want.

Wizard creates same names for test methods as in tested class. I think this is good approach. Normally we could add test_ prefix but there are only 30 characters available for method name.

7. Unit tests in eclipse

Eclipse does not have wizard for automatic tests creation. Initially I was missing that very much so I was creating unit tests from embed SAP GUI and then updating tests code again in eclipse. However after some time I learned how to write unit tests even more efficiently in eclipse and I do not need wizard any more. Eclipse offers dynamic and flexible templates. It is important to use them and be aware of that feature. These templates may be used for efficient code handling.

First difference that we see in eclipse are tabs on the bottom of source code, where we clearly see which part of class is global class (production code), which are local types or test classes. I like it, because it is easy now to find out place where to write different code.

To create class, just type "test" in "Test Class" view and press CONTROL + SPACE to see templates suggestions:

After choosing "testClass - Test class(ABAP Unit)" , initial version of test class is generated. In templates there are predefined variables which are marked with editor frame. We can jump by TAB pressing between variables and change names. Renaming single variable changes all occurrences in source code, that is convenient. In default template we need to update local test class name, test attributes and first method name. If we press enter, variables edition mode is finished and we have standard source code editor mode. Notice that default prefix for local class is "ltcl_" (local test class) and I use it as well.

Templates are very powerful and useful. It is possible to change existing templates or create new. They can be used for any development not only related to unit tests. Templates can be modified in " ABAP templates" settings in  Window->Preferences menu:

In my example standard testClass template is extended with mo_cut object, which is by default created in setup method as I know I use this pattern often.

With default template we have only one test. How to add new tests now? Quite simple. Just type new line in definition section:

METHODS my_second_test FOR TESTING.

Then point mouse course to my_second_test and press CONTROL + 1 (Quick Fix) and confirm with ENTER that you want to create new method:

This will create empty implementation of your method and cursor jumps inside that method so you can immediately start to write code. Initialize variables, execute method that you want to test and write assertion. You do not need to remember syntax of assertions, again templates come with help. Just type assert and press CONTROL + SPACE and choose assertEquals template. By default assertion is written in one line, I updated my template to have each parameter in new line as it suits my needs better.

These small tricks with CONTROL + SPACE and CONTROL + 1 for automated code generations speed up development much and makes me working faster than in SE80. Technically it is now easier to practice Test Driven Development (TDD) in eclipse as we can write tests first and design global class by creating empty methods by CONTROL + 1 Quick Fix feature from test class.

Unfortunatelythere is no wizard which automatically generates tests for all methods as in case of SE80. On the other hand, it is now up to developer to decide which methods should be tested and it does not take much time to auto-generate methods signatures. Other option is to use wizard from embed GUI.

8. Executing unit tests.

CONTROL + SHIFT + F10 is the shortcut worth to remember. It runs unit tests for current object. We can also execute unit tests on package level, by right click and Execute Tests option. In eclipse there is also convenient shortcut CONTROL + SHIFT + F11 which runs unit tests with code coverage, which is useful to see how well unit tests are covering tested class functionality. In SE80 we must run it from menu - Local Test Classes -> Execute -> Execute Tests with -> Code Coverage.

It is possible to schedule automated tests run by:

  • code Inspector (SCI) with check variant that contains unit tests execution,
  • rs_aucv_runner program where we can specify packages/programs and automatic emails notification.

9. Summary


Unit tests are important. However it is not so easy to start creating them. Once I compared learning unit tests to trip to high mountain - it is hard to climb, you may think it is not worth to try. But if you reach the top, there are beautiful views and you do not regret decision. This is how I feel - standing on top, being happy with my unit tests as part of daily development. Unit tests make you thinking wider about code aspects, you need to consider all input and output parameters. You see wider horizon as you are on the top of that mountain.

It is easier to start unit tests in SE80 because there is automatic wizard. On the other hand it is more efficient to write them in eclipse as there are flexible templates and code completion. I spent more time on explaining eclipse part not because it is more complex, but it has more potential and tricks that are worth to know.

If you want to write more advanced unit tests without database dependency, please read also my blog:

http://scn.sap.com/community/abap/testing-and-troubleshooting/blog/2013/03/21/abap-unit-tests-withou...

Good luck with unit testing!

13 Comments