Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Unit tests of local classes

Former Member
0 Kudos

Hello,

I tried to search for some info about this topic but i didnt find anything. Creating unit tests for global classes is easy since you can generate unit tests for existing methods and then set the data. Accessing private methods of the tested class is possible via this statement:

   CLASS tested_class DEFINITION LOCAL FRIENDS test_class.

This line is not directly in the code of the tested class, but it is in the local test class. And that is the problem with testing local classes - I dont know how to set a friend class outside the code of the tested class. I still get this error during activation:

"You can only define a PUBLIC class within a global CLASSPOOL. -"

I thought about two solutions but none of them is really satisfying:

1) making all tested methods public - against OOP concept

2) set tested methods which are private to protected and inherit from the tested class, so that the test class can "see" all methods. Again it violates OOP concept but it is imho much better solution. In this case I cannot test local classes with constructors that have parameters - unit test classes have to have parameterless constructor.

Can somebody tell me if there is a better solution to unit test local classes(then turning them to global classes)?

Thanks in advance for your help!

Martin

1 ACCEPTED SOLUTION

nomssi
Active Contributor

Dear Martin,

I avoid global classes if I can afford it, so I  have been using the following 'pattern' to avoid the problems you seem to have (if I understand your concern correctly):

  •      offer friendship to an INTERFACE instead of a concrete class.

I used to think it was from the wiki (ABAP Unit Best Practice), but I could not find it mentioned there, so here it goes:

define an empty interface, say

INTERFACE lif_unit_test.
ENDINTERFACE.

My local classes all offer friendship to this interface, e.g.

CLASS lcl_db_proxy DEFINITION CREATE PRIVATE
   FRIENDS lif_unit_test.
   PUBLIC SECTION.

...

  PRIVATE SECTION.

     METHODS constructor.
     METHODS update.
ENDCLASS

Now all my abap unit test classes need to access the private parts of the CUT (class under test) is to implementing this interface (quite easy as it is empty). So e.g.

CLASS lcl_db_proxy_test DEFINITION FOR TESTING.
   "#AU Risk_Level Harmless
   "#AU Duration Short
   PUBLIC SECTION.
     INTERFACES lif_unit_test.

   PRIVATE SECTION.
     DATA mo_db TYPE REF TO lcl_db_proxy.
...
     METHODS setup.
     METHODS update FOR TESTING.
ENDCLASS.

The setup method can now create and access private field of the CUT. With this, I do not care about the FRIEND reference while renaming the test class or adding a new one.

hope this helps,

J.N.N.

7 REPLIES 7

naimesh_patel
Active Contributor
0 Kudos

Is your local class within your Global Class or within your program? You should be able to add a statement, FRIENDS lcl_test_class to the LCL_CLASS_UNDER_TEST.

CLASS lcl_Test_class DEFINITION DEFERRED.
CLASS lcl_class_under_test DEFINITION LOCAL FRIENDS lcl_Test_class.

Regards,
Naimesh Patel

0 Kudos

Hello Naimesh,

there is no global class, I have some local classes in my program which I want to test. Those two lines just dont work in this case.

Martin

0 Kudos

It should work. You might be doing something wrong. Check out this program. It doesn't have any syntax error. If you run the unit test (Program > Test > Unit Test) you should see an error screen.

CLASS lcl_test DEFINITION DEFERRED.
*
CLASS lcl_sum DEFINITION FRIENDS lcl_test.
  PUBLIC SECTION.
    METHODS: sum IMPORTING iv_1 TYPE i
                 RETURNING value(rv_sum) TYPE i.
  PRIVATE SECTION.
    DATA: v_1 TYPE i.
    DATA: v_sum TYPE i.
    METHODS: do_sum.
ENDCLASS.                    "lcl_sum DEFINITION
*
CLASS lcl_test DEFINITION FOR TESTING
  "#AU Risk_Level Harmless
  "#AU Duration   Short
.
  PUBLIC SECTION.
    METHODS: m_sum FOR TESTING.
ENDCLASS.                    "lcl_test DEFINITION

*
CLASS lcl_sum IMPLEMENTATION.
  METHOD sum.
    v_1 = iv_1.
    do_sum( ).
    rv_sum = v_sum.
  ENDMETHOD.                    "sum
  METHOD do_sum.
*    v_sum = v_1 + v_1.
  ENDMETHOD.                    "do_sum
ENDCLASS.                    "lcl_sum IMPLEMENTATION
*
CLASS lcl_test IMPLEMENTATION.
  METHOD m_sum.
    DATA: o_cut TYPE REF TO lcl_sum.
    CREATE OBJECT o_cut.
    o_cut->v_1 = 2.
    o_cut->do_sum( ).

    cl_aunit_assert=>assert_equals(
        exp                  = 4
        act                  = o_cut->v_sum
        msg                  = 'something wrong'
           ).
  ENDMETHOD.                    "m_sum
ENDCLASS.                    "lcl_test IMPLEMENTATION

Regards,
Naimesh Patel

0 Kudos

This is possible of course, but I dont think that referencing a test class in tested class is the way to go. In case of tested global class there is no friend reference, it is defined in the test class include(but not in the class itself). That means that deleting this class leads to deleting also the friend reference. This is what I am trying to achieve(impossible?) with local class.

0 Kudos

Martin Jecminek wrote:

This is possible of course, but I dont think that referencing a test class in tested class is the way to go. In case of tested global class there is no friend reference,

I think you didn't check the Local Class include properly. Open your local class include in the global class. Very first two statements would establish the FRIEND relation between your Global class and Test class.

class lcl_Test_123 definition deferred.
class zcl_Test_Np definition local friends lcl_Test_123.

I tried this in various systems - both 700 and 702 and it did generate these above two statements. They are necessary while generating the Test class, if you are using any private attributes of the production class.

Martin Jecminek wrote:

That means that deleting this class leads to deleting also the friend reference. This is what I am trying to achieve(impossible?) with local class.

Deleting which class?

Regards,
Naimesh Patel

nomssi
Active Contributor

Dear Martin,

I avoid global classes if I can afford it, so I  have been using the following 'pattern' to avoid the problems you seem to have (if I understand your concern correctly):

  •      offer friendship to an INTERFACE instead of a concrete class.

I used to think it was from the wiki (ABAP Unit Best Practice), but I could not find it mentioned there, so here it goes:

define an empty interface, say

INTERFACE lif_unit_test.
ENDINTERFACE.

My local classes all offer friendship to this interface, e.g.

CLASS lcl_db_proxy DEFINITION CREATE PRIVATE
   FRIENDS lif_unit_test.
   PUBLIC SECTION.

...

  PRIVATE SECTION.

     METHODS constructor.
     METHODS update.
ENDCLASS

Now all my abap unit test classes need to access the private parts of the CUT (class under test) is to implementing this interface (quite easy as it is empty). So e.g.

CLASS lcl_db_proxy_test DEFINITION FOR TESTING.
   "#AU Risk_Level Harmless
   "#AU Duration Short
   PUBLIC SECTION.
     INTERFACES lif_unit_test.

   PRIVATE SECTION.
     DATA mo_db TYPE REF TO lcl_db_proxy.
...
     METHODS setup.
     METHODS update FOR TESTING.
ENDCLASS.

The setup method can now create and access private field of the CUT. With this, I do not care about the FRIEND reference while renaming the test class or adding a new one.

hope this helps,

J.N.N.

Former Member
0 Kudos

Yes that is exactly what I was trying to achieve and seems to me as a perfect solution. Thank you very much!