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: 

-A doubt on message handling in Test Driven Development

Hello,

I was trying adding my TDD in a very simple se38 program and stuck into an issue which I tried googling in forum but didn't find solution towards. So if some one has already encountered this problem, would be a great help if you please share your experience how did you resolve this piece.

Problem statement: In my legacy Program (not OOPs based) has a message statement inside a subroutine which is causing a failure to ABAP Unit test run.

Tried several approaches, seems not fruitful so far. As every time it is throwing an exception with CX_AUNIT_UNCAUGHT_MESSAGE.

May be missing something very basic!

Here the message statement is causing the issue as may be it is trying to connect with GUI, not sure.

I am handling this piece of code as below under Test Class/method:

But it is still failing and my expectation is, it should pass the test. As This message I need to be raised as per given test data and I don't want to pass the message as an exporting parameter from the process_data routine. I would like to leave the form untouched as much as possible.

Any pointers to this particular problem would be really helpful,.

Thank you. Som

1 ACCEPTED SOLUTION

thalesvb
Active Contributor

Hello,

Plain MESSAGE statements inside code under test are "forbidden" at unit testing.

Unit Test Runtime virtually runs in a session without dialog available; MESSAGE without RAISING or INTO additions, as is on your code snippet, depends on dialog (or being run in background), so it won't work here.

Results screen gives a (faint) tip on this case in analysis tab with a imprecise text: 'MESSAGE ... raised but not handled!' (I do know that this solely won't help at all). ABAP Unit tought that your MESSAGE statement already had a RAISING addition, and no one caught it to handle before reached main code of unit test runtime environment.

You need to change your code to propagate this message instead of directly showing inside this form:

  • Below 7.50 you can wrap inside your Exception class (LCX_OBJECT) when raising this exception (replace MESSAGE statement with RAISE EXCEPTION type LCX_OBJECT..., check RAISE EXCEPTION syntax; you can either pass as parameters or direcly define as a constant inside exception class).
  • If you are already on 7.50 you can rely on MESSAGE of RAISE EXCEPTION statement.
  • Serialize message into a "message protocol" (FORM parameter/internal table using a type like BAPIRET2) to be displayed on (main) outer code. Standard does it in a lot of function modules and all BAPIs that should return a success/error message to display.

And then adjust your code where this form is called to handle exception/form protocol to display it.

Best regards

12 REPLIES 12

FredericGirod
Active Contributor
0 Kudos

could you post the local test class definition & the test method definition ?

it is strange to have this kind of message in a test method

Peter_Inotai
Active Contributor

Shouldn't the MESSAGE statement in PROCESS_DATA have also the RAISING EXCEPTION addition if you expect an exception later?

Right observation thanks for pointing it out Peter. Actually I tried that but within form this is not getting accepted as Message with raising is non-class based whereas form raising ( as class based ). But this works fine within class / method. Thank You for your comment and help!

Peter_Inotai
Active Contributor
0 Kudos

BTW you could also use this assertion, that way you don't need a helper variable:

cl_abap_unit_assert=>assert_bound( act =exc ).

Thank you, yes agree a better way to handle this case if exception gets caught into... - Regards, Som

thalesvb
Active Contributor

Hello,

Plain MESSAGE statements inside code under test are "forbidden" at unit testing.

Unit Test Runtime virtually runs in a session without dialog available; MESSAGE without RAISING or INTO additions, as is on your code snippet, depends on dialog (or being run in background), so it won't work here.

Results screen gives a (faint) tip on this case in analysis tab with a imprecise text: 'MESSAGE ... raised but not handled!' (I do know that this solely won't help at all). ABAP Unit tought that your MESSAGE statement already had a RAISING addition, and no one caught it to handle before reached main code of unit test runtime environment.

You need to change your code to propagate this message instead of directly showing inside this form:

  • Below 7.50 you can wrap inside your Exception class (LCX_OBJECT) when raising this exception (replace MESSAGE statement with RAISE EXCEPTION type LCX_OBJECT..., check RAISE EXCEPTION syntax; you can either pass as parameters or direcly define as a constant inside exception class).
  • If you are already on 7.50 you can rely on MESSAGE of RAISE EXCEPTION statement.
  • Serialize message into a "message protocol" (FORM parameter/internal table using a type like BAPIRET2) to be displayed on (main) outer code. Standard does it in a lot of function modules and all BAPIs that should return a success/error message to display.

And then adjust your code where this form is called to handle exception/form protocol to display it.

Best regards

0 Kudos

Thanks and that my realisation too!

0 Kudos

Hello frdric.girod,

Thanks for your comment. Here you go:

could you post the local test class definition & the test method definition ?

Thank You, Som

Sandra_Rossi
Active Contributor
0 Kudos

Please post the code as text instead of image, so that one can easily answer by testing your code.

FredericGirod
Active Contributor

I have rebuild a full example with something like your code

You should not use MESSAGE statement in OOo code. You have to replace it with an exception class. I have wrote a code with exception class also. You could use this exception class to keep the message number/information ..

I have also wrote the proposal of peter.inotai (because it is better to do like that)

REPORT ztest_fg029.


CLASS lx_my_error DEFINITION INHERITING FROM cx_static_check .

  PUBLIC SECTION.
    INTERFACES if_t100_dyn_msg .
    INTERFACES if_t100_message .

    CONSTANTS:
      BEGIN OF failed_for_my_method,
        msgid TYPE symsgid      VALUE '00',
        msgno TYPE symsgno      VALUE '001',
        attr1 TYPE scx_attrname VALUE 'GV_ERROR_INFO',
        attr2 TYPE scx_attrname VALUE '',
        attr3 TYPE scx_attrname VALUE '',
        attr4 TYPE scx_attrname VALUE '',
      END OF failed_for_my_method.
    DATA gv_error_info TYPE string.

    METHODS constructor
      IMPORTING
        !textid       LIKE if_t100_message=>t100key OPTIONAL
        !previous     LIKE previous          OPTIONAL
        iv_error_info TYPE string             OPTIONAL.
ENDCLASS.



CLASS  lx_my_error IMPLEMENTATION.
  METHOD constructor ##ADT_SUPPRESS_GENERATION.
    CALL METHOD super->constructor
      EXPORTING
        previous = previous.
    gv_error_info = iv_error_info.
    CLEAR me->textid.
    IF textid IS INITIAL.
      if_t100_message~t100key = if_t100_message=>default_textid.
    ELSE.
      if_t100_message~t100key = textid.
    ENDIF.
  ENDMETHOD.
ENDCLASS.



CLASS lc_productive DEFINITION.

 PUBLIC SECTION.
    METHODS my_method        RAISING lx_my_error.
    METHODS my_second_method RAISING lx_my_error.
ENDCLASS.




CLASS lc_productive IMPLEMENTATION.

  METHOD my_method.
    MESSAGE e001(00)  WITH `My_Method failed hardly`.
  ENDMETHOD.

  METHOD my_second_method.
    RAISE EXCEPTION TYPE lx_my_error
      EXPORTING
        textid        = lx_my_error=>failed_for_my_method
        iv_error_info = `My_Method failed hardly`.
  ENDMETHOD.

ENDCLASS.


CLASS ltc_productive DEFINITION
      FOR TESTING
      RISK LEVEL HARMLESS
      DURATION SHORT
      FINAL.

  PRIVATE SECTION.
    methods setup.
    methods test_method_1           for testing raising lx_my_error.
    methods test_method_2           for testing raising lx_my_error.
    methods test_method_2_peter_com for testing raising lx_my_error.
    DATA o_cut          TYPE REF TO lc_productive.

ENDCLASS.


CLASS ltc_productive IMPLEMENTATION.
  method setup.
    o_cut = new #( ).
  endmethod.

  method test_method_1.
    try.
    o_cut->my_method( ).
      catch lx_my_error into data(lo_exception).
        data(lv_flag) = abap_true.
    endtry.
    cl_abap_unit_assert=>assert_equals(
      act = lv_flag
      exp = abap_true  ).
  endmethod.

  method test_method_2.
    try.
    o_cut->my_second_method( ).
      catch lx_my_error into data(lo_exception).
        data(lv_flag) = abap_true.
    endtry.
    cl_abap_unit_assert=>assert_equals(
      act = lv_flag
      exp = abap_true  ).
  endmethod.

  method test_method_2_peter_com.
    try.
    o_cut->my_second_method( ).
      catch lx_my_error into data(lo_exception).
    endtry.
    cl_abap_unit_assert=>assert_bound( lo_exception ).
  endmethod.
endclass.

0 Kudos

Many Thanks and I appreciate your response with some good example, useful. But my problem is with PERFORM/ FORM.

As form also can raise an exception, but that is class based and message can raise an exception which is non-class based.

So I can't propagate this exception back to the caller with a message followed by raising statement but with raise exception type..... ( class based) way.

I was trying to make this work as there are many legacy code which has this kind of scenarios and whether to have any option available ( as I am not aware of but someone already has resolved with some technique) else code realignment is needed.

Class based scenarios are bit easy I found as have many options ( one already you have shared) also inhering and redefining and handle under try~catch inside. But when this is FORM and which is not under a global FM / Class bit different to handle I felt.

Ravirajsinh1
Explorer
0 Kudos

Hey Som,

I see the question is here from some time now and you have already voted for one of the answer.

But I was just wondering, what is we propagate the message class, id and number to the caller of the subroutine. And the caller checks if message details are filled and then display the message.

I still believe using local exception would be the way to go. However as you have mentioned, this is a legacy code and one might not want to make more changes (as this could involve testing which can be expensive).

What are your thoughts on this?

Regards,

Raviraj