Calculate hash value for internal table
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' ).
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.
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:
Hello Sandra, thank you for your comment. I will test this out. This would also make the code a lot more readable.
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.
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
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.
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.
Thank you Jan! I needed a hash since internal tables cannot be part of a (containing) internal table's key.
I wrote a test on Sandra's suggestion to use EXPORT - for a large table (130K rows). Seems much quicker, by a factor of about seven.
Regarding Hans' 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
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):
Hello Constantinos Sourelis
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.