Dependent objects which can’t be controlled makes writing unit tests hard or even impossible. In unit test environments dependent objects should be replaced with test doubles. They imitate the behavior of the real objects. The graphic below illustrates this idea.

Testdouble1.PNG

Until now test doubles classes had to be written by hand. This can be quite a tedious process. The ABAP Test Double Framework solves this problem and makes it easier to write unit tests for your code.
The framework is available with SAP BASIS release 740 SP9 and higher.

What is it

The ABAP Test Double Framework is a standardized solution for creating test doubles and configuring their behavior. The framework supports the automatic creation of test doubles for global interfaces. Method call behavior can be configured using the framework API. It is possible to define the values of returning, exporting, changing parameters and raise exceptions or events. Additionally, the framework provides functionality to verify interactions on the test double object, e.g. the number of times a method was called with specific input parameters.

Quick Demo Video

Getting started

In this document we use an expense management application as an example. cl_expense_manager is one of the main classes in the application which is used for expense calculations. Expenses can be entered by the users in different currencies and the expense manager has methods to calculate the total expense in the required currency. The expense manager uses an object of if_td_currency_converter to get the real time currency conversion rates and then calculate the total expenses.  For testing methods of cl_td_expense_manager, we have to make sure that the method calls on the if_td_currency_converter object return exactly the values that we expect it to return. Otherwise the unit test would fail because the values on which assertions are being done are dependent on the values returned by the methods of the if_td_currency_converter interface. To achieve this, first we have to create a test double object for the if_td_currency_converter interface and inject it into the expense calculation class.
We will be using the if_td_currency_converter interface as the external api interface for which test doubles get created, throughout this document.

The example interface

INTERFACE if_td_currency_converter PUBLIC .

  EVENTS new_currency_code EXPORTING VALUE(currency_code) TYPE string.

  METHODS convert
    IMPORTING
              amount          TYPE i
              source_currency TYPE string
              target_currency TYPE string
    RETURNING VALUE(result)  TYPE i
    RAISING  cx_td_currency_exception.

  METHODS convert_to_base_currency
    IMPORTING
      amount          TYPE i
      source_currency  TYPE string
    EXPORTING
      base_currency    TYPE string
      base_curr_amount TYPE i.

ENDINTERFACE.

Let’s get started with the creation and the injection of the test double object.

Creating and Injecting the test double instance

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

  PRIVATE SECTION.
    METHODS:
      create_double FOR TESTING RAISING cx_static_check,

ENDCLASS.

CLASS ltcl_abap_td_examples IMPLEMENTATION.

  METHOD create_double.

    DATA: lo_currency_converter_double TYPE REF TO if_td_currency_converter,

          lo_expense_manager          TYPE REF TO cl_td_expense_manager.

    “create test double object
    lo_currency_converter_double ?= cl_abap_testdouble=>create( ‘if_td_currency_converter’ ).

    “injecting the test double into the object being tested
    CREATE OBJECT lo_expense_manager EXPORTING currency_converter = lo_currency_converter_double.

  ENDMETHOD.

ENDCLASS.

Please note that casting the test double object to the correct variable reference is very important. The example shows the injection of the test double object through the constructor, you can also use any other form of dependency injection.

Configuring outputs for method calls

The next step is to configure the behavior of the methods of the test double. We can configure specific output values for specific input values. The configuration of method call consists of two statements in sequence. The first statement is the configuration call. It’s primarily used by configuring the output values. The second statement is a call on the double object. It’s used to specify the input parameters.
The following example shows a simple configuration which specifies that 80 should be returned by the double if the convert method call gets called with the input: amount = 100 , source_currency = ‘USD’ and target_currency = ‘EUR’.

Simple configuration

  METHOD simple_configuration.

    DATA:  lo_currency_converter_double TYPE REF TO      if_td_currency_converter,
          lo_expense_manager          TYPE REF TO      cl_td_expense_manager,
          lv_total_expense            TYPE              i.

    “create test double object
    lo_currency_converter_double ?= cl_abap_testdouble=>create( ‘if_td_currency_converter’ ).

  “configuration for stubbing method ‘convert’:

    “step 1: set the desired returning value for the method call
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->returning( 80 ).

    “step 2: specifying which method should get stubbed
    lo_currency_converter_double->convert(
          EXPORTING
        amount          = 100
        source_currency = ‘USD’
        target_currency = ‘EUR’
        ).

    “injecting the test double into the object being tested
    CREATE OBJECT lo_expense_manager EXPORTING currency_converter = lo_currency_converter_double.

    “add one expense item
    lo_expense_manager->add_expense_item(
          EXPORTING
        description  = ‘Line item 1’
        currency_code = ‘USD’
        amount        = ‘100’
        ).

    “actual method call
    lv_total_expense = lo_expense_manager->calculate_total_expense( currency_code = ‘EUR’ ).

    “assertion
    cl_abap_unit_assert=>assert_equals( exp = 80 act = lv_total_expense ).

  ENDMETHOD.

The code inside the method calculate_total_expense calls the convert method of if_td_currency_converter. In the example the calls to the convert method always return 80 for the specified input parameters. By using a test double we make sure that currency conversion fluctuations in the real world does not affect our unit tests.

Example of different variants of configurations:

METHOD configuration_variants.

    DATA:  lo_currency_converter_double TYPE REF TO      if_td_currency_converter,
          lo_expense_manager          TYPE REF TO      cl_td_expense_manager,
          lv_total_expense            TYPE              i.

  “create test double object
    lo_currency_converter_double ?= cl_abap_testdouble=>create( ‘if_td_currency_converter’ ).

    “eg1: configuration for exporting parameters
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->set_parameter( name = ‘base_currency’  value = ‘EUR’
                                                                    )->set_parameter( name = ‘base_curr_amount’  value = 80 ).

    lo_currency_converter_double->convert_to_base_currency(
      EXPORTING
        amount          = 100
        source_currency = ‘USD’
    ).

    “eg2: configuration ignoring one parameter. 55 gets returned if source currency = ‘USD’ , target currency = ‘EUR’ and any value  for amount.
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->returning( 55 )->ignore_parameter( ‘amount’ ).
    lo_currency_converter_double->convert(
      EXPORTING
        amount          = 0 “dummy value because amount is a non optional parameter
        source_currency = ‘USD’
        target_currency = ‘EUR’
    ).

  “eg3: configuration ignoring all parameters. 55 gets returned for any input
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->returning( 55 )->ignore_all_parameters( ).
    lo_currency_converter_double->convert(
      EXPORTING
        amount          = 0 “dummy value
        source_currency = ‘USD’ “dummy value
        target_currency = ‘EUR’ “dummy value
    ).

  ENDMETHOD.

Please note that the configure_call  method is used to configure the next method call statement on the test double. If you need to configure different methods of an interface, the configure_call method should be called for every method.

Configuring exceptions for method calls

We can configure exceptions  to be raised for a method call with specific input parameters. To configure an exception, the exception object to be raised has to be instantiated and then added to the configuration statement.

  METHOD configuration_exception.

    DATA: lo_currency_converter_double TYPE REF TO if_td_currency_converter,
          lo_expense_manager          TYPE REF TO cl_td_expense_manager,
          lv_exp_total_expense        TYPE i,
          lo_exception                TYPE REF TO cx_td_currency_exception.

    FIELD-SYMBOLS: <lv_value> TYPE string.

    “create test double object
    lo_currency_converter_double ?= cl_abap_testdouble=>create( ‘if_td_currency_converter’ ).

    “instantiate the exception object
    CREATE OBJECT lo_exception.

“configuration for exception. The specified exception gets raised if amount = -1, source_currency = USD “and target_currency = ‘EUR’
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->raise_exception( lo_exception ).
    lo_currency_converter_double->convert(
      EXPORTING
        amount          = -1
        source_currency = ‘USD’
        target_currency = ‘EUR’
    ).

  ENDMETHOD.

Limitation:
Only class based exceptions are supported.

Configuring events for method calls

Events can be configured to be raised for method calls . The event name along with parameters and values (if any) has to be specified in the configuration statement.

METHOD configuration_event.

    DATA: lo_currency_converter_double TYPE REF TO if_td_currency_converter,
          lo_expense_manager          TYPE REF TO cl_td_expense_manager,
          lv_total_expense            TYPE i,
          lv_exp_total_expense        TYPE i,
          lt_event_params              TYPE abap_parmbind_tab,
          ls_event_param              TYPE abap_parmbind,
          lo_handler                  TYPE REF TO lcl_event_handler.

    FIELD-SYMBOLS: <lv_value> TYPE string.

    “create test double object
    lo_currency_converter_double ?= cl_abap_testdouble=>create( ‘if_td_currency_converter’ ).

    “configuration for event. ‘new_currency_code’ event gets raised if the source_currency = INR
    ls_event_param-name = ‘currency_code’.
    CREATE DATA ls_event_param-value TYPE string.
    ASSIGN ls_event_param-value->* TO <lv_value>.
    <lv_value> = ‘INR’.
    INSERT ls_event_param INTO TABLE lt_event_params.
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->raise_event( name = ‘new_currency_code’ parameters = lt_event_params
                                                                    )->ignore_parameter( ‘target_currency’
                                                                    )->ignore_parameter( ‘amount’ ).

    lo_currency_converter_double->convert(
      EXPORTING
        amount          = 0
        source_currency = ‘INR’
        target_currency = ”
    ).

  ENDMETHOD.

CLASS lcl_event_handler DEFINITION.

  PUBLIC SECTION.

    DATA: lv_new_currency_code TYPE string.

    METHODS handle_new_currency_code FOR EVENT new_currency_code OF if_td_currency_converter IMPORTING currency_code.

ENDCLASS.

CLASS lcl_event_handler IMPLEMENTATION.

  METHOD handle_new_currency_code.
    lv_new_currency_code = currency_code.
  ENDMETHOD.

ENDCLASS.

Limitation:
Default values for event parameters are not supported. If an event has to be raised with a default value for a parameter, the value has to be explicitly specified in the configuration statement.

Changing method call behavior based upon the number of calls

In the previous examples, we have learned how to configure the output parameters for a specific combination of input parameters. This means that the configured output gets returned by the test double for any number of calls to the method with the specified input parameters. But there can be cases where the output may have to change depending on the number of calls. This can be achieved with adding the times method to the configuration statement.

Configuring method call behavior for a specific number of calls

METHOD configuration_times.

    DATA:  lo_currency_converter_double TYPE REF TO      if_td_currency_converter,
          lo_expense_manager          TYPE REF TO      cl_td_expense_manager,
          lv_total_expense            TYPE              i.

    “create test double object
    lo_currency_converter_double ?= cl_abap_testdouble=>create( ‘if_td_currency_converter’ ).

    “configuration for returning 80 for 2 times
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->returning( 80 )->times( 2 ).
    lo_currency_converter_double->convert(
      EXPORTING
        amount          = 100
        source_currency = ‘USD’
        target_currency = ‘EUR’
    ).

    “configuration for returning 40 the next time
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->returning( 40 ).
    lo_currency_converter_double->convert(
      EXPORTING
        amount          = 100
        source_currency = ‘USD’
        target_currency = ‘EUR’
    ).

  ENDMETHOD.

The previous example configures the double to return 80 for the first two calls and then 40 for the third call on the method.
Please note the following behavior for the configurations:
1. If times is not specified in the configuration, it is implied to be 1.
2. If a call comes exceeding the number of times specified, then the output of the last matching configuration is returned. For example, in the above example 40 would be returned for 4th call.

Verifying Interactions

We can also set expectations on the interactions on the test double and verify these expectations at the end of the test. This is helpful in conditions when the number of calls of specific interface methods needs to be tracked. The conditions to be verified can be configured by chaining and_expect method in the configuration statement. The actual verification against the expectations is done at the end of the test with the verify_expectations method call.

METHOD verify_interaction.

    DATA: lo_currency_converter_double TYPE REF TO if_td_currency_converter,
          lo_expense_manager          TYPE REF TO cl_td_expense_manager,
          lv_total_expense            TYPE i,
          lv_exp_total_expense        TYPE i VALUE 160.

    “create test double object
    lo_currency_converter_double ?= cl_abap_testdouble=>create( ‘if_td_currency_converter’ ).

    “injecting the test double into the object being tested
    CREATE OBJECT lo_expense_manager EXPORTING currency_converter = lo_currency_converter_double.

    “add three expenses
    lo_expense_manager->add_expense_item(
      EXPORTING
        description  = ‘Line item 1’
        currency_code = ‘USD’
        amount        = ‘100’
    ).

    lo_expense_manager->add_expense_item(
      EXPORTING
        description  = ‘Line item 2’
        currency_code = ‘USD’
        amount        = ‘100’
    ).

    lo_expense_manager->add_expense_item(
      EXPORTING
        description  = ‘Line item 3’
        currency_code = ‘INR’
        amount        = ‘100’
    ).

  “configuration of expected interactions
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->returning( 80 )->and_expect( )->is_called_times( 2 ).
    lo_currency_converter_double->convert(
      EXPORTING
        amount          = 100
        source_currency = ‘USD’
        target_currency = ‘EUR’
    ).

    “actual method call
    lv_total_expense = lo_expense_manager->calculate_total_expense( currency_code = ‘EUR’ ).

    “assertion
    cl_abap_unit_assert=>assert_equals( exp = lv_exp_total_expense act = lv_total_expense ).

    “verify interactions on testdouble
    cl_abap_testdouble=>verify_expectations( lo_currency_converter_double ).

  ENDMETHOD.

In the previous example, the method verify_expectations will raise an error message if the convert method gets called less than 2 times. The framework always raises error messages as early as possible. If the convert method gets called for the 3rd time it would immediately raise a message and won’t wait till the verify_expectations method gets executed.

Advanced Topics

Implementing Custom Matchers

The framework always matches the configured behavior with actual calls by using a default implementation of the if_abap_testdouble_matcher interface. It uses the ABAP EQ operator for matching the input parameters. However, in some use cases this may not be sufficient. The framework supports custom matchers where a user can configure how the input values get evaluated by the framework. This functionality is important if there are objects passed as arguments to a method or if there has to be some user specific logic for matching. Custom matchers have to implement the if_abap_testdouble_matcher interface. The matcher object gets used by adding the set_matcher method in the configuration statement. It tells the framework to replace the default matcher with the custom matcher. When an actual method call happens the framework calls the match method passing the actual arguments and the configured arguments for all methods to evaluate equality. The custom matcher provides the logic to evaluate the equality in the match method.

Custom matcher class implementation

CLASS lcl_my_matcher DEFINITION.

  PUBLIC SECTION.
    INTERFACES if_abap_testdouble_matcher.

ENDCLASS.

CLASS lcl_my_matcher IMPLEMENTATION.

  METHOD if_abap_testdouble_matcher~matches.

    DATA : lv_act_currency_code_data  TYPE REF TO data,
          lv_conf_currency_code_data TYPE REF TO data.

    FIELD-SYMBOLS:
      <lv_act_currency>  TYPE string,
      <lv_conf_currency> TYPE string.

    IF method_name EQ ‘CONVERT’.

      lv_act_currency_code_data = actual_arguments->get_param_importing( ‘source_currency’ ).
      lv_conf_currency_code_data = configured_arguments->get_param_importing( ‘source_currency’ ).

      ASSIGN lv_act_currency_code_data->* TO <lv_act_currency>.
      ASSIGN lv_conf_currency_code_data->* TO <lv_conf_currency>.

      IF <lv_act_currency> IS ASSIGNED AND <lv_conf_currency> IS ASSIGNED.
        IF <lv_act_currency> CP <lv_conf_currency>.
          result = abap_true.
        ENDIF.
      ELSE.
        result = abap_false.
      ENDIF.

    ENDIF.

  ENDMETHOD.

ENDCLASS.

Using the custom matcher in a configuration

  METHOD custom_matcher.

    DATA: lo_currency_converter_double TYPE REF TO if_td_currency_converter,
          lo_expense_manager          TYPE REF TO cl_td_expense_manager,
          lv_total_expense            TYPE i,
          lv_exp_total_expense        TYPE i VALUE 160,
          lo_matcher                  TYPE REF TO lcl_my_matcher.

    “create test double object
    lo_currency_converter_double ?= cl_abap_testdouble=>create( ‘if_td_currency_converter’ ).

  “configuration
    CREATE OBJECT lo_matcher.
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->returning( 80 )->set_matcher( lo_matcher ).
    lo_currency_converter_double->convert(
      EXPORTING
        amount          = 100
        source_currency = ‘USD*’
        target_currency = ‘EUR’
    ).

  “injecting the test double into the object being tested
    CREATE OBJECT lo_expense_manager EXPORTING currency_converter = lo_currency_converter_double.

  “add expenses with pattern
    lo_expense_manager->add_expense_item(
      EXPORTING
        description  = ‘Line item 1’
        currency_code = ‘USDollar’
        amount        = ‘100’
  ).

    lo_expense_manager->add_expense_item(
      EXPORTING
        description  = ‘Line item 2’
        currency_code = ‘USDLR’
        amount        = ‘100’
    ).

    “actual method call
    lv_total_expense = lo_expense_manager->calculate_total_expense( currency_code = ‘EUR’ ).

    “assertion
    cl_abap_unit_assert=>assert_equals( exp = lv_exp_total_expense act = lv_total_expense ).

  ENDMETHOD.

Implementing Custom Answers

The framework provides the possibility to completely influence the output/result of a method call with user specific coding. The custom answer has to implement the if_abap_testdouble_answer interface and the answer object gets used by adding the set_answer method to the configuration statement. The answer method is called by the framework when the input values of an actual method call matches a configuration. The answer method has all the logic to provide the output values. Exporting, changing, returning values and exceptions can be set on the result object. Furthermore, events can be raised using the double_handle object.

Custom answer class implementation

CLASS lcl_my_answer IMPLEMENTATION.

  METHOD if_abap_testdouble_answer~answer.
    DATA : lv_src_currency_code_data TYPE REF TO data,
          lv_tgt_currency_code_data TYPE REF TO data,
          lv_amt_data              TYPE REF TO data,
          lt_event_params          TYPE abap_parmbind_tab,
          ls_event_param            TYPE abap_parmbind.

    FIELD-SYMBOLS:
      <lv_src_currency_code> TYPE string,
      <lv_tgt_currency_code> TYPE string,
      <lv_amt>              TYPE  i,
      <lv_value>            TYPE string.

    IF method_name EQ ‘CONVERT’.

      lv_src_currency_code_data = arguments->get_param_importing( ‘source_currency’ ).
      lv_tgt_currency_code_data = arguments->get_param_importing( ‘target_currency’ ).
      lv_amt_data = arguments->get_param_importing( ‘amount’ ).

      ASSIGN lv_src_currency_code_data->* TO <lv_src_currency_code>.
      ASSIGN lv_tgt_currency_code_data->* TO <lv_tgt_currency_code>.
      ASSIGN lv_amt_data->* TO <lv_amt>.

      IF <lv_src_currency_code> IS ASSIGNED AND <lv_tgt_currency_code> IS ASSIGNED AND <lv_amt> IS ASSIGNED.

        IF <lv_src_currency_code> EQ ‘INR’ AND <lv_tgt_currency_code> EQ ‘EUR’.

          result->set_param_returning( <lv_amt> / 80 ).

        ENDIF.

      ENDIF.

    ENDIF.

  ENDMETHOD.

ENDCLASS.

Adding the custom answer implementation to a method call configuration

METHOD custom_answer.

    DATA: lo_currency_converter_double TYPE REF TO        if_td_currency_converter,
          lo_expense_manager          TYPE REF TO        cl_td_expense_manager,
          lv_total_expense            TYPE              i,
          lv_exp_total_expense        TYPE              i                                              VALUE 25,
          lo_answer                    TYPE REF TO        lcl_my_answer.

    “create test double object
    lo_currency_converter_double ?= cl_abap_testdouble=>create( ‘if_td_currency_converter’ ).

    “instantiate answer object
    CREATE OBJECT lo_answer.

  “configuration
    cl_abap_testdouble=>configure_call( lo_currency_converter_double )->ignore_parameter( ‘amount’ )->set_answer( lo_answer ).
    lo_currency_converter_double->convert(
      EXPORTING
        amount          =  0
        source_currency = ‘INR’
        target_currency = ‘EUR’
    ).

  “injecting the test double into the object being tested
    CREATE OBJECT lo_expense_manager EXPORTING currency_converter = lo_currency_converter_double.

  “add the expense line items
    lo_expense_manager->add_expense_item(
      EXPORTING
        description  = ‘Line item 1’
        currency_code = ‘INR’
        amount        = ’80’
      ).

    lo_expense_manager->add_expense_item(
      EXPORTING
        description  = ‘Line item 2’
        currency_code = ‘INR’
        amount        = ‘240’
    ).

    lo_expense_manager->add_expense_item(
      EXPORTING
        description  = ‘Line item 3’
        currency_code = ‘INR’
        amount        = ‘800’
    ).

    lo_expense_manager->add_expense_item(
      EXPORTING
        description  = ‘Line item 4’
        currency_code = ‘INR’
        amount        = ‘880’
    ).

    “actual method call
    lv_total_expense = lo_expense_manager->calculate_total_expense( currency_code = ‘EUR’ ).

    “assertion
    cl_abap_unit_assert=>assert_equals( exp = lv_exp_total_expense act = lv_total_expense ).

  ENDMETHOD.

The framework currently supports the creation of test doubles for global interfaces. Support for non-final classes is already under discussions.

Have a look at the framework and feel free to give your feedback or ask questions in the comment section.

To report this post you need to login first.

26 Comments

You must be Logged on to comment or reply to a post.

  1. Christian Dr. Drumm

    Hi Prajul,

    nice document.

    In my opinion the test double framework should definitely be extended to also support global, non-final classes. There are many cases where no interfaces are available, especially in legacy code. Requiring an interface makes using the test double framework unnecessarily complex.

    Best,

    Christian

    (0) 
      1. Kai Meder

        I thought this is a core requirement for anything that generates mocks…
        Relying on interfaces solely is really green-field 😉

        (0) 
    1. Peter Inotai

      I guess it was also downported to 7.40 from some higher internal release (eg 7.50, 7.60 or 8.0x).

      Anyway it would be nice to make it available for 7.30 and 7.20/7.02.

      (0) 
    2. Thomas Alexander Ritter

      Hi Tapio,

      we already did a downport to SAP BASIS 731 but it has not been officially released, yet. During the design and implementation phase of the framework we made sure that it can be downported to 7.02 as well. However, there are currently no plans for such a downport.

      cheers

      Thomas

      (0) 
      1. Tapio Reisinger

        Hi Thomas,

        sounds good. I believe there will be a lot of customers who will stay for lower Basis releases than 7.40 for a quite long time. So I think there is a great demand for a framework like this. Especially when you find out how useful TDD is…

        Best regards, Tapio

        (0) 
  2. Jacques Nomssi

    Hello Prajul,

    this framework will really simplify behavior verification.

    I cannot give any feedback yet, but I have a question: would it be possible to extend the functionality to local classes and interfaces? Is there a technical limitation there?

    Thanks for sharing.


    best regards,

    Jacques

    (0) 
  3. Iegor Titov

    As far as I understand ‘generate subroutine pool’ is used each time for proxy object generation, however how the generation limit (maximum of 36 temporary subroutine pools) is revoked?

    (0) 
    1. Uwe Kunath

      Dear Iegor,

      From what I’ve been seeing so far, the limit is there and applies also to the test double framework. However, subroutine pools are cached per interface, once the first proxy class has been generated. This means, per interface, you can have as many test doubles as you want, as the method outputs are not hard coded, but reseolved by a parameter handler internally. Only the second or third interface that you want test doubles being created for counts as number 2 or 3, until the subroutine limit is reached. This should certainly be enough 🙂

      (0) 
  4. Damir Majer

    Hi Prajul,

    really nice introduction! I like it a lot 😉

    Is there any official SAP Help Documentation planned regarding describing this topic official like for example the ABAP Unit Topics?

    By the way, do you know when (or better if) the suggested creation of the testdouble over an global (why not also for local context…) class is making any progress?

    Thanks in advance for an reply.

    Best regards,

    Damir

    (0) 
  5. Suhas Saha

    Hi Prajul,

    I am on ABAP Release 740 SP 12, but the package SABP_UNIT_TEST_DOUBLE_CORE is still marked as “strictly internal” 😕

    /wp-content/uploads/2016/03/2016_03_14_102738_907227.jpg

    Does the framework (still) support only global interfaces?

    BR,

    Suhas

    (0) 
    1. Thomas Alexander Ritter

      Hi,

      good catch! Sorry, we forgot to update the description. This has been fixed in the newer releases. There is actually a simple way for checking whether it is supported or not. See if package interface: SABP_UNIT_TD_API is available. It defines all interfaces/classes which are released for consumption.

      cheers

      Thomas

      (0) 
      1. Suhas Saha

        Hi Thomas,

        The package interface is available 😉

        Now back to the burning question,

        Does the test double framework support (non-final) global classes?

        I can see that the interface IF_ATD_PROXY_GENERATOR is implemented by –

        • CL_ATD_PROXY_GENERATOR_CLASS
        • CL_ATD_PROXY_GENERATOR_INTF

        But the factory method CL_ATD_PROXY_FACTORY=>CREATE_PROXY( ) supports only interfaces 😯

        /wp-content/uploads/2016/03/2016_03_14_165633_907619.jpg

        Bummer! Or am i missing something?

        BR,

        Suhas

        (0) 
  6. Gabor Farkas

    This looks very nice and it was about time SAP introduced their own mocking framework. As others mentioned a downport would be great (along with support for classes, not just interfaces).

    (0) 
  7. Paul Hardy

    In regard to mocking an exception, from my testing it looks like the exception has to be declared in the signature of the method being mocked?

    Some classes raise a “NO_CHECK” exception (for better or worse) or sometimes a DYNAMIC_CHECK which is not mentioned in the signature (definitely for the worse). I gather such behaviour cannot be mocked using the ATDF?

    Downporting to 7.02 would be wonderful, I will be on that release until 2025, in regard to classes as well as interfaces I am not so fussed, because one of the greatest computer programmers of all time, Joe Dolce, raised the very valid point that if your class does not implement any interfaces then it has gotta no respect.

    Cheersy Cheers

    Paul

    (0) 
    1. Maximilian Wernli

      I have the same problem. The framework throws an error ( “The configured exception … is not declared in method …”) when I want it to raise an unchecked exception.
      Maybe the devs forgot about NO_CHECK? I hope this will be fixed soon, like this it is not possible to verify my error handling with the ATDF, I have to use mockA for that.

       

      (0) 

Leave a Reply