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: 
suketu_dave
Employee
Employee
In this blog series, I will share my understanding and knowledge about ABAP Unit Testing and TDD approach. As this itself is a vast topic, I will first begin with Unit testing fundamentals, then delve into testing framework. Later I will write about TDD, its necessity and illustrate how to achieve it practically. The blog series is structured in such a way that it goes from scratch, and later goes in-depth into the concepts.

Here are the links to the other blogs in this blog series:



















Blog No Blog Title with embedded link
1 Understanding ABAP Unit Testing Fundamentals – Overview for Beginners
2 “Test Doubles” and using OSQL Test Double Framework for ABAP Unit Tests
3 Implementing Unit Tests for an ABAP Report Program

In this blog post, I will give an introduction about unit testing fundamentals and the basic concepts of Unit Testing. I will also give an example of a very simple unit test in ABAP, covering positive and negative scenario. This blog is particularly for absolute beginners who would like to start off in a systematic way to Unit Testing in ABAP.


  1. 5 WH Analysis of Unit Testing




To begin with a brief introduction, let me first answer the 5 WH questions for Unit Testing.


Table 1: 5 WH Analysis of Unit Testing


























What Approach for testing the behavior of a unit of production code
Why

To verify whether the independent behavior of every single unit of the code is as per the expectations.

To cover all the positive and negative scenarios, covering all those which cannot be tested otherwise.
Who Developers
When During the development stage, preferably as part of Test-Driven Development (TDD)
How

By restricting the communication of the unit from all other artifacts, which it is dependent upon, for its successful execution.

Following FIRST Principles.

Using ABAP Unit Test Framework, Mocking the dependencies, Test Doubles, Wrappers, Test Seams, Code Refactoring



  1. “Unit” in Unit Testing




Before going into the concepts of unit testing, we need to have a clear idea about what a “Unit” means, in the term “Unit Testing”.


Unit is basically the smallest piece of the code, that can be logically isolated and tested in such an isolation.


In ABAP, a unit can be a method, function module, subroutine etc.


Each unit does its intended tasks, by means of the logic written within it. This will result in certain effects, depending upon the given conditions. These effects can broadly be any of the two below:


Table 2: What to verify in Unit Test Results


















Effect What to verify in the unit test results
Change the state of the system Changes made to the concerned variables
Communication with other units Whether the call / invocation is made or not

 


  1. Fundamental concepts and terminologies in Unit Testing





  • CUT – Code Under Test / Class Under Test


As the name suggests, this is the actual production code which is to be tested. In Object-Oriented Programming, this CUT is usually the class.




  • LTC – Local Test Class


We are supposed to define and implement a unit testing class to implement unit testing test our CUT. This test class, which is local to the CUT is called as the Local Test Class (LTC). It is very important to mark this class for testing, by using the “FOR TESTING” keyword in the definition, so that at the runtime it is executed as test class.




  • UTM – Unit Test Methods


These are the methods of the LTC. They can be either public or private. It is very important to mark these methods for testing, by using the “FOR TESTING” keyword in the definition, so that at the runtime these are executed as test methods.


As mentioned in the 5 WH analysis, we test the behaviors of our production code.


It is evident that any given unit will exhibit multiple behaviors depending on the scenario. Each such scenario is a “Test Case”. These are classified as positive and negative test cases.


Unit test methods will invoke the corresponding unit of the CUT. As a good practice, it is suggested to write one unit test method for one scenario for one unit.



Figure 1: Interaction of LTC with CUT




  • FIRST Principles


FIRST is an acronym for - Fast, Independent, Repeatable, Self-validating, Timely.


Unit tests should comply with the below mentioned FIRST Principles:


Table 3: FIRST Principles of Unit Testing


























Fast The test cases should be very quick in execution, including setup, actual test and teardown time. Setup and teardown will be explained a bit later in this blog
Independent One test case should not depend upon other test case for execution. Thus there is no interference amongst the test cases. This also ensures that there is no dependency of order of execution of test cases
Repeatable The test cases should return the same result each time they are executed
Self-validating The unit test itself should convey whether it has passed or failed. In case of failure, the test should point out the location of failure
Timely This is related to Test Driven Development (TDD) approach where we write the unit tests before the actual production code is written


  • DOC – Depended-On-Component


The CUT will possibly interact with various artifacts (which are not defined in the unit) like DB Tables, DDIC Views, CDS Views, File system, Web Services, trigger output, invoke other units etc. These are the components, on which the CUT is dependent on, for its successful execution. These are termed as the DOCs.


However, according to the FIRST principles, the unit tests should be independent and fast.


Also, there are some difficulties if the LTC access the DOCs while execution, the details of which will be addressed in the next blog.


We prefer to mock all these dependencies by test doubles. These mocks / doubles of the DOC will replace the actual DOC during the unit test runtime. OSQL Test Double Framework handles this very well in the test environment when the DOC is a database object or a CDS view. In order to keep things simple in this blog, I have not mentioned the details here. I will provide a deeper look into this in the next blog.




  • Assertion


By its meaning, the word “assertion” means “a statement that you strongly believe to be true”.


As seen in the 5 WH analysis, we verify whether the unit test result is as per the expectations.


In ABAP, we use the static methods of class cl_abap_unit_assert. We pass the actual and expected values as parameters to these methods. So, we make our assertions for the behavior (result of the unit test) and pass them as parameters.




  • Test Fixtures


A test fixture is something which provides a test environment to the entity which is to be tested, so that the test executes consistently each time it is run on the entity. Test fixtures are used in multiple areas, be it software, electronics, manufacturing etc.


Test fixture methods play a vital role in the structure of Unit Test modules.


In unit testing, we have to setup the test environment and the testing frameworks. In ABAP, we use setup() and class_setup() methods.


At the end of the test we destroy (teardown) this setup. This is done using the teardown() and class_teardown() methods.


Test fixtures are defined and implemented in the private section of the ABAP LTC. Let us see a short description of each of these fixture methods:


Table 4: Test Fixtures for ABAP Unit Testing






















class_setup()

  • Static method. Executes once for the test class.

  • Invoke utility class method to setup the test double framework in the test environment for all the DOCs.

  • Example:  environment = cl_osql_test_environment=>create()


setup()

  • Instance method. Executes before the execution of each UTM.

  • Here we generally create a fresh new instance of the CUT.

  • Also, the data of the test doubles is defined here.

  • Since this method runs once before each UTM, we get a fresh new instance of the CUT, as well as fresh data for the test doubles. This ensures that one unit test doesn’t affect the execution of any other unit test.


class_teardown()

  • Static method. Executes once for the LTC.

  • Destroy the test environment.

  • Example:  environment->destroy()


teardown()

  • Instance method. Executes before the execution of each UTM.

  • Here we destroy the doubles and the instance of the LTC, which were created in the setup() method.

  • Example:  environment()->clear_doubles()




  • The 4-Phase test approach


Consider a real-life scenario that you are giving an online examination, where your scores will be displayed immediately after the exam:




  1. The first thing you do is complete the necessary revisions, setup your system for the test, arrange the required stationery, peace out yourself etc. This is the “Setup” phase.

  2. Then, the test begins. Within the specified time frame, you solve the questions, mark the answers and submit the test. This is the “Exercise” phase.

  3. Now, your submitted answers are evaluated. Your answers are verified with the expected answers and grades are given accordingly. The system displays your scorecard. This is the “Assert” phase.

  4. After getting your scores you close the system, leave the exam center, go home and keep aside all the study material you referred for this exam. This is the “Teardown” phase.


Unit tests are structured according to the 4-Phase approach – Setup, Exercise, Assert, Teardown:


Setup: Setup the test fixture








      • setup()

      • class_setup()






Exercise: Interact with CUT


Assert: Do necessary things to verify whether the expected outcome has been obtained


Teardown: Teardown the test fixture








      • teardown()

      • class_teardown()








  1. Example for practical demonstration




I will now show you a very simple example to get a clearer understanding of the theory mentioned above.


Here, I have created a class ZCL_DEMO_UNIT_TEST_SESSION with one method GET_YRS_OF_SRVC.



Figure 2: Class Under Test for demo example


This method takes in date of hire of an employee and provides the years of service. It has one importing parameter and two exporting parameters. The method signature and implementation are shown below.



Figure 3: Production method definition


Let us now find out positive and negative scenarios (test cases) for this method.


The importing parameter here is the date of hire. So, a possible negative test scenario will occur if the date of hire is a future date. Positive test case is thus when the date of hire is a date in the past, or even the current date. As clearly seen in the code, in the positive case, the method returns the years of service. And in the negative case it will return a message. Once this is clear, we can now start writing the LTC.


As far as the naming for the LTC goes, as a suggested good practice, we name it as ltc_<name of CUT, dropping the “cl_” prefix>


In this case, the LTC is - ltc_demo_unit_test_session



Figure 4: LTC Definition


Let me walk you through this definition of LTC. As we all know, any type of class has two parts – definition and implementation.


Similarly, we first write the class definition for our LTC. However, do you notice some extra key-words in Line 2?


These are the keywords which distinguish a ABAP test class from a usual ABAP class. Let us have a look at them one-by-one.


FOR TESTING : This addition is used to define a class as a test class


The below two are the test attributes.


RISK LEVEL HARMLESS : This indicates the effect which the test will have on the database. In short, the amount of risk that the test poses on the underlying database.


There are 3 risk levels defined – Critical, Dangerous, Harmless.


DURATION SHORT : The maximum time limit for execution of the entire LTC. This is to ensure that the unit tests don’t run for too long and thus comply with the FIRST principles.


There are 3 duration types defined – Short, Medium, Long


At the end of the blog I will mention about the transaction code SAUNIT_CLIENT_SETUP, where the customizing for these test attributes is maintained. For now, let us continue with our LTC.


As we have identified one positive and one negative test case, so in the public section there will be a total of 2 UTMs. Again, we add the keyword “FOR TESTING” here. As a suggested good practice, we should name these methods in a systematic way as mentioned in FIGURE. This will improve the readability and maintenance efforts for your LTC.


In the private section we define our test fixtures. I had mentioned earlier that we instantiate our CUT in the setup() method. For this, we define an object here in the private section and later use this object to create and destroy the instance of CUT.


Now coming to the implementation of this LTC.



Figure 5: LTC Implementation - Test Fixtures


Since there are no DOCs in this simple example, the static methods class_setup() and class_teardown() are empty.


In the setup() method we are instantiating the CUT and subsequently clearing this instance in teardown().


Below is the implementation of both the test methods.



Figure 6: LTC Implementation - Positive test case



Figure 7: LTC Implementation - Negative test case


As you can see here, there are 3 sections for any test case – Given, When, Then.


Table 5: Sections of ABAP Unit Test


















Given

Here we provide the value for the importing variables of our production method, according to the test case.

Also, we do the necessary data declaration to create variables which are necessary to handle the data returned by the production method.
When

Here we invoke the production method of our CUT using the instance of CUT.

We also provide the necessary importing parameters and receive the returned data (actual values) back from the method.
Then

Now that we have executed the method, we make the necessary assertions to compare the actual and expected behavior.

This will indicate whether the test case has executed successfully or not.

To execute the unit test, follow the menu options as shown in Figure 8 .


On executing the unit tests with coverage, we get the various coverage metrics as shown in Figure 9.



Figure 8: How to execute Unit Test with coverage



Figure 9: Coverage metrics


This was a practical illustration of a very simple unit testing scenario, just to get familiar with ABAP unit testing and keep things simple for beginners. In reality we have much more complex scenarios where we use approaches like test doubles, wrappers and test seams.


In my next blog blog post, I will cover test doubles and Open SQL Test Double Framework. Before closing the blog I will mention about the transaction SAUNIT_CLIENT_SETUP as I had mentioned earlier.



Figure 10: Transaction SAUNIT_CLIENT_SETUP


Limit of risk level: Indicates what is the maximum risk level of test classes that can be executed in this particular client. As shown in Figure 10, for this client the limit is set to “Critical”. Thus, tests with all the 3 risk levels are parmitted to run in this client.


Long, Medium, Short Duration: Indicate the maximum time in seconds for which the unit tests should preferrably run. If the LTC takes longer than this time to execute, system issues a warning message after the execution.


In the above example, the risk level of our test class is “harmless” and duration is “short”. This means that this test can run in this client given its risk level, and the test duration is not supposed to extend beyond 60 seconds, thus complying with FIRST principles.

18 Comments
michael_koehler2
Participant
Hi Suketu,

Even though ABAP UNIT has been around for quite a while (I remember a project where I used it in  2005...), it has also been quite a while since I have actually used it. There is always something more urgent... So it is a good thing even for older ABAPers to go back to the basics every now and then.

Keep it up,
Mike
chairat_onyaem
Participant
People are asking me about how to start writing unit test and TDD in ABAP. This would be a useful that they can start with. Thank you for this blog.
suketu_dave
Employee
Employee
0 Kudos
Thanks. Glad to know that this is helping out.

Hey Suketu!!

Really nice! I appreciate the time you put in and thanks for sharing this!

BR,

Shishira

suketu_dave
Employee
Employee
0 Kudos
Thanks a lot Shishira!!
sitakant_tripathy2
Active Participant
Hi Suketu,

fundamental and brilliant at the same time, Wonderfully articulated...just have one ask, when u dive onto test doubles could u create a matrix and extend the approach to handle various scenarios like integration, data queries, standard function calls and stuff.

I guess this is where most of the struggle is and any insights to standard frameworks to ease the dev effort might be of help.

Regards,

Sitakant.
suketu_dave
Employee
Employee
0 Kudos

Hi Sitakant,

Thank You for your feedback and suggestions.

Regarding your request – I have updated the blog with the link to my next blog in the series. This explains about data queries scenario.

Also, blogs to cover the other scenarios will be posted soon one-by-one, as I am still in the process to frame the content for these. Will keep updating my blog with the links to the future blogs.

Happy Learning!

Regards,
Suketu

jscote_saaq
Discoverer
Nice tutorial,

Thank you !

 

Regards,

JSCôté
former_member183260
Participant
Good article
sitakant_tripathy2
Active Participant
Thanks a lot Suketu, sorry had been away for a bit.

Regards,

Sitakant.
BaerbelWinkler
Active Contributor
A bit late to the party, but better late than never! So: thanks Suketu for your blog post! I think that I had read it shortly after you posted it right around the time I actually dabbled with unit tests in earnest for the first time.

When I added unit tests, I wanted to better understand how they work and what the "flow" through the code is. I therefore placed breakpoints in the unit test code. But, when I then executed them and actually spent "some" time in looking at the unit test code while in debug mode, I regularly ran into "time-outs" of sorts because the time allowed for short unit tests had obviously been exceeded rather quickly.

Is there a way to switch off the time constraint while debugging unit tests w/o having change access to SAUNIT_CLIENT_SETUP or editing the  code to allow medium or long duration (which of course wouldn't really make sense to dabble with for something like debugging anyway).

I'm working in 750 systems with EHP8.

Cheers

Bärbel
suketu_dave
Employee
Employee
0 Kudos
Hi Bärbel,

Thanks for your comment. Yes!! Even I face this warning every time i debug a test case.

Actually, as of now there is no means to control this behaviour. If you have a look at the FIRST principles above, the warning is issued as a measure to ensure that the 'F - Fast' characteristic is followed properly.

Otherwise, one could just switch the time constraint off, and write time consuming unit tests or so, and ship them out from the dev system. This is how I personally perceive this time constraint warning as. Also, you are right, changing SAUNIT_CLIENT_SETUP configurations, editing the code to medium or long duration etc. do not make sense here.

So, I would recommend you to continue with your debugging and enjoy understanding the flow, while not letting the warning bother you.

Happy Learning!

Regards,
Suketu.
roshangupta
Explorer
suketu.dave  Nice blog ! One best blog on AUnit.
Hi, I know CDS Views are included so far. Do you have any news when this will include AMDPs or table functions as well?
vasantha_s
Employee
Employee
0 Kudos
Read few others blogs got confused with too many complex. This blog is just prefect for beginners and I could understand basic concepts. Thanks a lot!
studencp
Participant
0 Kudos
nice blog, but in my opinion code unit testing is utopia good for academic divagations

in real life you will write more testing code than the tested code and anyway you will not cover all options, moreover in many scenarios it is not applicable (UI, workflows, etc.)
ioan_radulescu5
Explorer
0 Kudos
Good stuff. I got a bit confused with all the abbreviations, but it’s good stuff nevertheless. I only recently started to look into UT (haha) classes and I love it.

 

Thank you.
ioan_radulescu5
Explorer
0 Kudos
You can do workflows with this. You can wait for their execution in the class. It’s a good first step insurance policy for any developer.