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: 
JanBraendgaard
Active Participant

Introduction


This might not be groundbreaking tech-news, but it could benefit someone diving into ABAP unit testing for the first time.

From time to time the result of the method under test would be an internal table. And how to use this in the assertion for unit testing? Well, calculate the hash value for the entire internal table, and voila - assertion is possible.

 

Method to calculate hash value


METHODS:
get_hash_value_for_itab
IMPORTING
it_table TYPE ANY TABLE
EXPORTING
ev_hash TYPE hash160.
METHOD get_hash_value_for_itab.
DATA: lv_string TYPE xstring,
lv_xbuffer TYPE xstring.

" Clear the hash value
CLEAR ev_hash.

" Go through the table one line at a time
LOOP AT it_table ASSIGNING FIELD-SYMBOL(<ls_line>).
DATA(lv_index) = 0.
CLEAR lv_string.

" Concatenate all the columns of the line into one string
DO.
ASSIGN COMPONENT lv_index OF STRUCTURE <ls_line> TO FIELD-SYMBOL(<lv_field>) CASTING TYPE x.
IF sy-subrc EQ 0.
CONCATENATE lv_string <lv_field> INTO lv_string IN BYTE MODE.
ADD 1 TO lv_index.
ELSE.
EXIT.
ENDIF.
ENDDO.

" Add the string from the line to our buffer
CONCATENATE lv_xbuffer lv_string INTO lv_xbuffer IN BYTE MODE.
ENDLOOP.

" Calculate the hash value for the entire buffer/table
CALL FUNCTION 'CALCULATE_HASH_FOR_RAW'
EXPORTING
data = lv_xbuffer
IMPORTING
hash = ev_hash
EXCEPTIONS
unknown_alg = 1
param_error = 2
internal_error = 3
OTHERS = 4.
IF sy-subrc <> 0.
" Do error handling here
ENDIF.
ENDMETHOD.

Usage of method


DATA:
lv_hash_old TYPE hash160,
lv_hash_new TYPE hash160.

me->get_hash_value_for_itab(
EXPORTING
it_table = gt_final_req
IMPORTING
ev_hash = lv_hash_old ).

IF lv_hash_old <> lv_hash_new.
“ There's a difference
ENDIF.

 
cl_aunit_assert=>assert_equals(
act = lv_hash
exp = '5CAF285B3FA1C3F971BBEEEE1D61EF9095B6F4CC'
msg = 'Hash value for output not correct'
).

Conclusion


When I use this I first validate that the internal table I wish to calculate hash for is correct. Then in debug I fetch the hash value for the internal table, and hardcode this into my assertion.

 
10 Comments
Sandra_Rossi
Active Contributor
Thanks for this nice utility. I'm not sure in which case it can be useful for the unit tests, but anyway it may be useful in a number of situations.

Also, maybe using the following code is a little bit faster for serializing the internal table, but I don't know if it can be used for a later hash comparison:
EXPORT itab = it_table TO DATA BUFFER lv_xstring COMPRESSION OFF.
chairat_onyaem
Participant
Thanks for this. I’m looking for a way to get the hash value of the primary key to compare if two records are the same record and this will be very useful.
RAF
Active Contributor
0 Kudos
Hi,

in unit test you can compare internal tables directly with assert_equals. I find the result better readable than some hast value which is different
JanBraendgaard
Active Participant

Hello Robert, thank you for your comment. My challenge here was that I did not have the two tables available at the same time, so I could not make the comparison directly with assert_equals method.

BR, Jan

JanBraendgaard
Active Participant
0 Kudos
Hello Sandra, thank you for your comment. I will test this out. This would also make the code a lot more readable.

BR, Jan.
hans-dieter_loew
Explorer
Hello together,

the line
EXPORT itab = it_table TO DATA BUFFER lv_xstring COMPRESSION OFF.

will return different results in unicode and non-unicode systems/different releases.

The only way that I found to get the same hash values in unicode and non-unicode system/different releases was the idea from Jan.

 

BR Hans-Dieter
Constantinos
Explorer
Thank you jan_petersen! I needed a hash since internal tables cannot be part of a (containing) internal table's key.

I wrote a test on sandra.rossi's suggestion to use EXPORT - for a large table (130K rows). Seems much quicker, by a factor of about seven.

Regarding hans-dieter.loew' comment on intra-system hash preservation, I can see how EXPORT's serialization can be dependent on the release, in general.
However, wouldn't also the "loop serialization" end up with a different byte sequence between unicode and non-unicode systems (the former using two bytes per character)?

Note the loop can be made faster by not iterating over the components and using instead
ASSIGN COMPONENT 0 OF STRUCTURE ... CASTING TYPE x

But, at least for a short structure like I used, the speedup isn't much.

Another advantage of EXPORT is that it works for internal tables containing, e.g., string types (which would raise cx_sy_assign_cast_illegal_cast in the loop).

 

Results from a system running kernel 753. (I am also testing cl_abap_message_digest=>calculate_hash_for_raw which allows for SHA-2 hashes):

JanBraendgaard
Active Participant
Hello constantinos

Thank you for the work on timing the different ways. I'm pretty sure that your thoughts on the difference between unicode and non-unicode byte sequence are correct. I haven't tested this out though, as I primarely use this hashing on the same system.

BR, Jan
bence_toth
Employee
Employee
0 Kudos
I needed a method for transaction SAAB Checkpoint Groups, to generate a SUBKEY that is the same for the same callstack during logging (To generate only a single log entry for the same callstack).

For Dynamic Logpoints there is a similar function, called hash_stack( 😞
Dynamic Logpoints in ABAP

Thanks to this method, I am able to implement the same functionality as hash_stack( )!
matt
Active Contributor
0 Kudos
This was my use case just now. I'm glad I refound this blog!