Application Development and Automation 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: 
Read only

ABAP Unit - Testing internal tables

Former Member
0 Likes
4,665

Hi,

I am new in ABAP Unit. I want to create an ABAP Unit Test Class for testing subroutines.

One of the subroutines responsibles for uploading a file into an internal table.

Most of the subroutines work with internal tables (Inputs: internal tables, Outputs: internal tables).

I have read a lot posts about it, but I couldn't find any example about testing internal tables.

I have created a local test class and test methods:


**********************************************************************

**********************************************************************

class test_class definition

   inheriting from cl_aunit_assert

   for testing"#AU Risk_Level Harmless

   "#AU Duration Short

   private section.

     data:

           lt_upload_test  type standard table of ty_betolto"expected table

           lt_upload          type standard table of ty_betolto"actual table, filled by test_uploadfile test method

           ld_filename      type string.   "it contains filename

     methods test_uploadfile   for testing"upload input

     methods test_changefile  for testing"upload expected file + calling my subroutine + compare actual and expected tables

endclass.    

**********************************************************************

**********************************************************************

**********************************************************************

**********************************************************************

class test_class implementation.

  method test_uploadfile.

    ld_filename = 'C:\upload.txt'.

   call method cl_gui_frontend_services=>gui_upload

      exporting

        filename                     = ld_filename

        filetype                       = 'ASC'

        has_field_separator   = 'X'

      changing

        data_tab                    = lt_upload.           

    assert_subrc(  exp = 0

                            act = syst-subrc

                           msg = 'Error upload file!').   " this time I checked number of records, lt_upload = 11576 records

  endmethod.  

  method test_changefile.

    ld_filename = 'C:\upload_test.txt'.


   call method cl_gui_frontend_services=>gui_upload

      exporting

        filename                   = ld_filename

        filetype                     = 'ASC'

        has_field_separator = 'X'

      changing

        data_tab                   = lt_upload_test.        " this time I checked number of records, lt_upload_test = 11576 records


   perform changefile changing lt_upload.       " this time I checked number of records, lt_upload = 0 record

   assert_equals(  act = lt_upload

                            exp = lt_upload_test

                           msg = 'Error changing file').  " failed.

  endmethod.

endclass.

**********************************************************************

**********************************************************************

I hope anyone can help me.

Thanks for your help.

Best Regards, Alex

1 ACCEPTED SOLUTION
Read only

Former Member
0 Likes
3,429

Hi Alex,

You want to check whether internal table contents of lt_upload_test and lt_upload using the subroutine changefile.

Am i right?

The reason for lt_upload is initial(zero records) is that you haven't passed the internal table lt_upload to the method test_changefile.

I hope you will be calling the method test_uploadfile first. This will keep the entire data in the internal table lt_upload. But this will not be visible in the test_changefile method.

As a solution, you need to return the internal table lt_upload from the method test_uploadfile and pass the internal table lt_upload to the method test_changefile.

Cheers

~Niranjan

21 REPLIES 21
Read only

Former Member
0 Likes
3,430

Hi Alex,

You want to check whether internal table contents of lt_upload_test and lt_upload using the subroutine changefile.

Am i right?

The reason for lt_upload is initial(zero records) is that you haven't passed the internal table lt_upload to the method test_changefile.

I hope you will be calling the method test_uploadfile first. This will keep the entire data in the internal table lt_upload. But this will not be visible in the test_changefile method.

As a solution, you need to return the internal table lt_upload from the method test_uploadfile and pass the internal table lt_upload to the method test_changefile.

Cheers

~Niranjan

Read only

0 Likes
3,429

Hi Niranjan,

Thanks for your help.

So, I have to provide an output from an input. Simplified exapmle:

Input:

Record     Material     Price

1               54345      

2               43322    

3               34453

4               43322

...

Expected Output (It comes from an old processing program):

Record     Material     Price

1               54345       325

2               43322       290

3               34453       120

4               43322       290

...

My Output (It comes from my new processing program):

Record     Material     Price

1               54345       345  " Should be: 325

2               43322       300  " Should be: 290

3               34453       120  " Correct

4               43322       300  " Should be: 290

...

Process:

1. I upload an input (both programs got the same input)

2. I upload an expected output (it cames from old processing program)

3. I call my subroutine with the input (it changes the input), and it provides an output, that I compare with the expected output.

"I hope you will be calling the method test_uploadfile first. This will keep the entire data in the internal table lt_upload. But this will not be visible in thetest_changefile method."

How does the ABAP Unit calling methods, if I push Ctrl+Shift+F10 (what order)?

It this the only way to test an file interface based program?

Thanks for your reply.

Regards, Alex

Read only

0 Likes
3,429

Hi guys,

But this will not be visible in the test_changefile method.

lt_upload is a private attribute of the local class, and if it is filled in the method test_uploadfile then it is visible in test_changefile, you don't need to pass it to this method

Read only

0 Likes
3,429

Hi!

Thanks for your reply. I think so, too.

But I have no idea, why doesn't  lt_upload visible in  test_changefile.

Like I wrote before test_uploadfile shows gui_upload uploaded successfully the input file (11576 records), but  test_changefile show 0 record in the lt_upload internal table.

Do you have any idea?

Thank a lot for you time.

Regards, Alex

Read only

0 Likes
3,429

You should first call test_upload and then call test_changefile method otherwise yor internal table will still be initial

Read only

0 Likes
3,429

Hi,

I have created these test methods with for testing attributives:

method test_uploadfile.

method test_changefile.

Should I call them somewhere? Don't ABAP Unit do it for me? Isn't enough to press Ctrl+Shift+F10?

Thanks for you reply!

Alex

Read only

0 Likes
3,429

You can implement your local class in any executable program,FG, module pools and etc, from there you create an instance of the class but since, your methods are private, you might need to define a public method from which you call your methods, or define your methods as public ones.

....

DATA:  go_test type ref to test_class definition.

create object go_test.

"// If you redefine your methods to be public then,

go_test->test_uploadfile( ).

go_test->test_changefile( ).

"// i would change the lt_upload to MT_upload, and make it public.

perform changefile changing go_test->lt_upload.

.....

hope this helps..

Read only

0 Likes
3,429

Hi Kairat,

I believe you are mistaken about what the original poster is trying to do. He is trying to create ABAP Unit test classes, which are called by the ABAP Unit test runtime. There is no need to call the FOR TESTING methods directly.

Read only

0 Likes
3,429

Yes, you are right.

I have an ABAP program with subroutines. In the same source code I have implemented my Test Class as well with these test methods.

But I have problems with internal table content, like I wrote it above.

I have no idea what's wrong with these methods. Any idea?

Thanks guys your reply.

Alex

Read only

0 Likes
3,429

Yes you are right, you can call it the other way as well, however, even if you write the code as I've written, on productive system the code is ignored.

and by the way why do  you have a perform in a method of test class?

if you want to run them in a required order you can do it as I've proposed.

Read only

0 Likes
3,429

Note: I don't believe its the case that the test code is ignored in a productive system. I believe the code is not compiled in a productive system. I don't know for certain, but I would assume that as a result, if you include calls to test classes in non-test code, the non-test code will fail to compile in production as well. Does anyone know for certain if that's the case?

Read only

alex_campbell
Contributor
0 Likes
3,429

Hi Alex,

ABAP Unit does not guarantee that the FOR TESTING methods are executed in any particular order. Even though you have defined test_uploadfile first, and test_changefile second, you cannot assume that they will be executed in that sequence. It's possible that test_changefile is executed first, and test_uploadfile is executed second.

At any rate, you shouldn't be making your test_changefile test dependant on the result of you rest_uploadfile test. The changefile logic could be 100% correct, but it would fail if there was a flaw in the uploadfile logic. This dependency is counter to the purpose of a Unit Test, which is to test each unit of functionality independant of the others.

Hope this helps. To be honest, your test methods as they are written above don'e make much sense to me (what subroutine is test_uploadfile testing?) If you'd like some further advice, let me know!

Read only

0 Likes
3,429

Hi Alex,

Thanks for your time. This is my fault, I didn't want write full source code here, and I only wanted to know how can I compare internal tables (which come from uploded text files).

My program logic is like this (these steps subroutines):

1. upload text file (about 13000 records)

2. checking text file (if there is some mistake, it corrects and deletes some of them by conditions)

3. pricing (it gives them price by business logic)

4. assigning (it assigns a value for all records)

5. make a batch input

So I have 5 subroutines. In the same source code I implemented a Test Class. In the test methods I am calling the subroutines and comparing the result and the expected records.

I understand I should make them independent, but they are working with internal tables, and passing them between each other.

You are right, these methods don't make any sense, I only wrote it, to ask questions about it.

Thanks.

Read only

alex_campbell
Contributor
0 Likes
3,429

I took a closer look at your test methods. How are you passing the filename to perform changefile? Unless you're using a global constant or an initialized global variable, it may not be set when the test methods execute.

Also, I think you should be able to set a breakpoint in the test methods, and debug through them when you execute the unit tests.

Read only

0 Likes
3,429

I don't know what happened yesterday, but I couldn't set any breakpoints in the test methods. Today I could set BP and it was interesting, like you said before test_changefile runs before test_uploadfile. So it was the problem.

But still I don't know how I could make them independent, but I will work on it.

Thanks a lot.

Read only

nomssi
Active Contributor
0 Likes
3,429

Hi Alex,

methods test_uploadfile   for testing"upload input

this is not a test method. It is a setup method to create your fixture.

I have refactored your test:

CLASS test_class DEFINITION
    INHERITING FROM cl_aunit_assert
    FOR TESTING"#AU Risk_Level Harmless
                  "#AU Duration Short
   PRIVATE SECTION.
     TYPES tt_data TYPE STANDARD TABLE OF ty_betolto.

     METHODS get_file_date  " not a test method
       IMPORTING iv_fname TYPE c
       CHANGING ct_data TYPE tt_data.
    
     METHODS test_changefile FOR TESTING.
ENDCLASS.

CLASS test_class IMPLEMENTATION.

   METHOD get_file_data.
     DATA ld_filename TYPE string.   "it contains filename

     ld_filename = iv_fname.
     CLEAR ct_data[].

     CALL METHOD cl_gui_frontend_services=>gui_upload
       EXPORTING
         filename            = ld_filename
         filetype            = 'ASC'
         has_field_separator = 'X'
       CHANGING
         data_tab            = ct_data.
   ENDMETHOD.

   METHOD test_changefile.
     DATA: lt_upload_test  TYPE tt_data"expected table
           lt_upload       TYPE tt_data"actual table

*   Expected result
     get_file_data(
       EXPORTING iv_fname = 'C:\upload_test.txt'
       CHANGING ct_data = lt_upload_test ).
*   Fixture
     get_file_data(
       EXPORTING iv_fname = 'C:\upload.txt'
       CHANGING ct_data = lt_upload ).
*   Test
     PERFORM changefile CHANGING lt_upload.
*   Check
     assert_equalsact = lt_upload
                     exp = lt_upload_test
                     msg = 'Error changing file')
   ENDMETHOD.     

ENDCLASS

This should work but the next problem is that you rely on data on the file system (drive C:\) for your test.

I would maintain the data directly, e.g (assuming fields named MATERIAL and PRICE for type TY_BETOLTO)

CLASS test_class DEFINITION
    INHERITING FROM cl_aunit_assert
    FOR TESTING"#AU Risk_Level Harmless
                  "#AU Duration Short
   PRIVATE SECTION.
     TYPES tt_data TYPE STANDARD TABLE OF ty_betolto.

     METHODS expected_result CHANGING ct_data TYPE tt_data.
     METHODS fixture CHANGING ct_data TYPE tt_data.
    
     METHODS test_changefile FOR TESTING.
ENDCLASS.

CLASS test_class IMPLEMENTATION.

   DEFINE add_data.
     CLEAR ls_data.
     ls_data-material = &1.
     ls_data-price = &2.
     APPEND ls_data TO ct_data.
   END-OF-DEFINITION.
  
   METHOD expected_result.
     DATA ls_data TYPE LINE OF tt_data.
    
     CLEAR ct_data[].
*Record     Material     Price
*1               54345       325
*2               43322       290
*3               34453       120
*4               43322       290
     add_data: '54345' '325',
               '43322' '290',
               '34453' '120',
               '43322' '290'.
   ENDMETHOD.

   METHOD fixture.
     DATA ls_data TYPE LINE OF tt_data.

     CLEAR ct_data[].
*Record     Material     Price
*1               54345      
*2               43322    
*3               34453
*4               43322
     add_data: '54345' '',
               '43322' '',
               '34453' '',
               '43322' ''.
   ENDMETHOD.

   METHOD test_changefile.
     DATA: lt_upload_test  TYPE tt_data"expected table
           lt_upload       TYPE tt_data"actual table

     expected_result( CHANGING ct_data = lt_upload_test ).
     fixture( CHANGING ct_data = lt_upload ).
*   Test
     PERFORM changefile CHANGING lt_upload.
*   Check
     assert_equalsact = lt_upload
                     exp = lt_upload_test
                     msg = 'Error changing file')
   ENDMETHOD.     

ENDCLASS.

I hope this helps.

JNN

Read only

Former Member
0 Likes
3,429

Hi!

Thanks for your reply. The only problem with your second solution, I have about 13000 records in my input file, so it would be a nightmare to hardcode it.

You are right it's not a test method. I took your advice, and made a CLASS-METHOD class_setup and CLASS-DATA internal tables, and uploaded all text files in class_setup method.

Now I have all text files in internal tables:

- input

- expected_output_2 (testing subroutine 2)

- expected_output_3 (testing subroutine 3)

- expected_output_4 (testing subroutine 4)

- expected_output_5 (testing subroutine 5)

But it's still problem, my subroutines are depend from each other.

Thanks for your help.

Read only

nomssi
Active Contributor
0 Likes
3,429

Hi Alex,

you need 4 test methods, one for each subroutine. The layout of each routine should like in my  proposal:

  • fill the expected result table, if all you data are in files, you can use my first proposal with different file name for each test.
  • fill the input table,
  • call the SUT (subroutine(s) under test),
  • check the result with assert.

The methods cannot depend on each other. They can depend only other non-test method in the unit test.

Since you use the CLASS_SETUP routine to update all files at once, you HAVE to use a fresh copy of your INPUT_TABLE in each test.

You can also be lazy and use the SETUP routine instead, it will be called before each test routine.

If your subroutines depend on each other, call them all in the right sequence before checking the results, e.g.

fixture(  CHANGING ct_data = input_table ). " Fill input table

* call the SUT

PERFORM subroutine2 CHANGING input_table.

PERFORM subroutine3 CHANGING input_table. " depends on subroutine 2

PERFORM subroutine4 CHANGING input_table. " depends on subroutine 3

PERFORM subroutine5 CHANGING input_table. " now: the SUT, depends on subroutine 4

* Load the expected results

expected_result( CHANGING ct_data = exp_table ).

* Check

assert( ... ).

hope this helps,

JNN

Read only

Former Member
0 Likes
3,429

I got an idea (maybe one of you metioned above):

I have 4 subroutine to test. So I create 4 test classes.

class testclass_1.

method setup.

   "  upload input text file

   "  upload expected file

endmethod.

method test_sub1.

     perform sub1.

     assert_equals().

endmethod.

endclass.

class testclass_2:

method setup.

   "  upload input text file

   "  upload expected file_2

endmethod.

method test_sub2.

     perform sub1.

     perform sub2.

     assert_equals().

endmethod.

endclass.

class testclass_3:

method setup.

   "  upload input text file

   "  upload expected file_3

endmethod.

method test_sub3.

     perform sub1.

     perform sub2.

     perform sub3.

     assert_equals().

endmethod.

endclass.

class testclass_4:

method setup.

   "  upload input text file

   "  upload expected file_4

endmethod.

method test_sub4.

     perform sub1.

     perform sub2.

     perform sub3.

     perform sub4.

     assert_equals().

endmethod.

endclass.

What do you think? Thanks guys, I am really appreciate your help.

Read only

nomssi
Active Contributor
0 Likes
3,429

What do you think?

I think this will work. The tests are now independent.

I think this is more an acceptance test than a typical unit test. It is not repeatable without special files on your local drive, so it is not automated.

First make it work, then make it right. I think you will have to implement all test in a single test class or you will stop maintaining the tests because it takes too long to adapt (cf TAOUT)

JNN

Read only

Former Member
0 Likes
3,429

Hi Jacques,

Thanks for your reply, I am getting understand TTD, and the purpose of Unit Test. So now I understood my tests are acceptance tests instead of Unit Tests.

I started to read this book that you linked above, thanks for the link.

Thanks guys for your replies!

Alex