Skip to Content
Technical Articles
Author's profile photo Mike Pokraka

Test Doubles are coming to Function Modules

More of an announcement rather than a blog: Drumroll for the latest in Unit Testing Frameworks, Function Modules.

Function modules have always been the thorn in the side of anyone trying to write ABAP Unit Tests. The only sensible way to mock a call to a function module was to wrap the FM into a class method and then mock that, either via the OO Test Double Framework or by coding explicit test doubles.

Well, the next shiny new version of ABAP (7.56) includes a Function Module test double. And while I could write a nice blog with examples, SAP have stolen my thunder and done a sterling job of documenting it in good detail with great examples over here. Personally I really like the simple, easy to understand way the framework is structured.

For those on 7.56, enjoy! The rest of us will have to be very patient. But at least for those on lower versions it’s always handy to know what’s coming in order to avoid spending too much resources on reinventing a future wheel.

Have Fun!

 

Update: Following a great hint from Thomas Fiedler that this is available in the ABAP Cloud trial system, I managed to give it a little go. I used a simple method that calls BAPI_MESSAGE_GETDETAIL and returns the message text.

  method get_message.

    data: message type bapiret2-message,
          return  type bapiret2.

    call function 'BAPI_MESSAGE_GETDETAIL'
      exporting
        id         = '00'
        number     = 000
        textformat = 'HTM'
      importing
        message    = message
        return     = return.

    result = message.

  endmethod.

A quirk I discovered was that, mocking a call where I don’t care about the input to the FM proved to be more complex than setting up a scenario with expected input parameters. One needs to implement interface if_ftd_invocation_answer instead of just calling create_input_configuration.

For convenience and brevity I implemented the interface in the test class so I could control everything in one class and pass me as the answering object, thus:

class ltc_test definition final for testing
  duration short
  risk level harmless.

  public section.
    interfaces if_ftd_invocation_answer.

  private section.
    constants mock_text type string value `The moon is made of green cheese`.

    methods fm_is_mocked for testing raising cx_static_check.

endclass.


class ltc_test implementation.

  method fm_is_mocked.

    data(function_double) = cl_function_test_environment=>create(
      value #( ( 'BAPI_MESSAGE_GETDETAIL' ) ) )->get_double( 'BAPI_MESSAGE_GETDETAIL' ).

    function_double->configure_call( )->ignore_all_parameters( )->then_answer( me ).

    cl_abap_unit_assert=>assert_equals( act = new zcl_message( )->get_message( )
                                        exp = mock_text ).

  endmethod.

  method if_ftd_invocation_answer~answer.
    result->get_output_configuration( )->set_exporting_parameter( name  = 'MESSAGE' 
                                                                  value = mock_text ).
  endmethod.

endclass.

 

Assigned Tags

      13 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Andrea Borgia
      Andrea Borgia

      Wanting to write unit tests on a lower release, though, was a great learning experience: I had to design an interface to abstract DB operations and feed test data via helper methods.

      Sadly, only a small group of developers (and companies) actually care about tests during development.

      Author's profile photo Mike Pokraka
      Mike Pokraka
      Blog Post Author

      Thanks for your comments. Agree testing and code quality in general still seems to be undervalued in many projects.

      I had the same learning experience, and is not just about unit tests, but how they help you to write better code.

      Author's profile photo Suhas Saha
      Suhas Saha

      ... is not just about unit tests, but how they help you to write better code

      +1.

      Not only do they provide the so-called "safety net" against regression, but they also make your code cleaner and more structured.

      Author's profile photo Sandra Rossi
      Sandra Rossi

      To make it clear to everyone, from the link you posted: "ABAP function module test double framework is available since back-end version SAP NetWeaver AS for ABAP 7.56."

      Author's profile photo Mike Pokraka
      Mike Pokraka
      Blog Post Author

      Thanks Sandra, I did mention it in the last paragraph, but have added the version number to the main text to be clear.

      Author's profile photo Sandra Rossi
      Sandra Rossi

      Is 7.6 same as 7.56?

      Author's profile photo Mike Pokraka
      Mike Pokraka
      Blog Post Author

      Argh! Yes of course, I meant 7.56. Corrected, thanks!

      Author's profile photo Suhas Saha
      Suhas Saha

      I have been using it for sometime now. IMO, the mocking technique used in FTDF (SET...WHEN...THEN) is more polished than that of the ATDF.

       

      Author's profile photo Mike Pokraka
      Mike Pokraka
      Blog Post Author

      Agree, I always did find he class framework a bit clunky and this one is much nicer for the reader too.

      Author's profile photo Thomas Fiedler
      Thomas Fiedler

      You can play around with the new framework on the Steampunk Trial.

      Regards,Thomas.

      Author's profile photo Mike Pokraka
      Mike Pokraka
      Blog Post Author

      Great hint, thanks! Played, success, updated blog.

      Author's profile photo Suhas Saha
      Suhas Saha

      Hi Mike,

      A quirk I discovered was that, mocking a call where I don’t care about the input to the FM proved to be more complex than setting up a scenario with expected input parameters.

      You can also configure the output, also when you ignore all the input parameters. I don't think that you need to implement the interface IF_FTD_INVOCATION_ANSWER explicitly.

        method FM_IS_MOCKED.
          data(FUNCTION_DOUBLE) = FUNCTION_TEST_ENVIRONMENT->GET_DOUBLE( |BAPI_MESSAGE_GETDETAIL| ).
      
          data(MESSAGE_OUTPUT_CONFIG) = FUNCTION_DOUBLE->CREATE_OUTPUT_CONFIGURATION(
          )->SET_EXPORTING_PARAMETER( NAME = |MESSAGE| VALUE = MOCK_TEXT ).
      
          FUNCTION_DOUBLE->CONFIGURE_CALL(
          )->IGNORE_ALL_PARAMETERS(
          )->THEN_SET_OUTPUT( MESSAGE_OUTPUT_CONFIG ).
      
          CL_ABAP_UNIT_ASSERT=>ASSERT_EQUALS(
            ACT = new LCL_MESSAGE( )->GET_MESSAGE( )
            EXP = MOCK_TEXT ).
      
        endmethod.
      Author's profile photo Mike Pokraka
      Mike Pokraka
      Blog Post Author

      Hi Suhas,

      Understood, but you are using the then_set_output method. From the method names I would infer a simpler syntax using then_answer , something like:

      function_double->configure_call( 
          )->ignore_all_parameters( 
          )->then_answer( name  = 'MESSAGE' 
                          value = mock_text ).

      Or an even shorter nice to have:

      function_double->configure_call( )->answer( name  = 'MESSAGE' 
                                                  value = mock_text ).

      Either of these would be more compact and easier to understand, and would cover many of my use cases.