‎2012 Jul 19 9:15 AM
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
‎2012 Jul 19 10:15 AM
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
‎2012 Jul 19 10:15 AM
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
‎2012 Jul 19 10:46 AM
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
‎2012 Jul 19 2:28 PM
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
‎2012 Jul 19 2:42 PM
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
‎2012 Jul 19 2:55 PM
You should first call test_upload and then call test_changefile method otherwise yor internal table will still be initial
‎2012 Jul 19 3:05 PM
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
‎2012 Jul 19 3:27 PM
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..
‎2012 Jul 19 3:33 PM
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.
‎2012 Jul 19 3:39 PM
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
‎2012 Jul 19 3:46 PM
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.
‎2012 Jul 19 4:08 PM
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?
‎2012 Jul 19 3:42 PM
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!
‎2012 Jul 20 8:46 AM
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.
‎2012 Jul 19 4:25 PM
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.
‎2012 Jul 20 8:53 AM
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.
‎2012 Jul 19 10:50 PM
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_equals( act = 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_equals( act = lt_upload
exp = lt_upload_test
msg = 'Error changing file').
ENDMETHOD.
ENDCLASS.
I hope this helps.
JNN
‎2012 Jul 20 9:28 AM
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.
‎2012 Jul 20 10:41 AM
Hi Alex,
you need 4 test methods, one for each subroutine. The layout of each routine should like in my proposal:
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
‎2012 Jul 20 10:56 AM
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.
‎2012 Jul 22 7:59 PM
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
‎2012 Jul 23 9:56 AM
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