Erch
Erch

Reputation: 625

Mock sy-uname in ABAP Unit test

I am trying to write my first unit tests in ABAP.

My method under test fetches team members of the logged-on user (via system variable sy-uname).

Since I want the test to run for everyone I can't just let the method run and assert one of my own team members.

I want to mock sy-uname, so the test is not dependent on who is executing it.

Is this possible? If yes how do you mock system parameters?

Upvotes: 4

Views: 2382

Answers (3)

Barkas67
Barkas67

Reputation: 1

With regards to replace just one system variable I would go for test seams.

Encapsulating system variables by an interface, requires caller to instantiate the class, maybe to pass the instance over multiple methods as parameter. This causes an increased complexity and decline of performance. This might be justified if there are scenarios with the need of other values than the system variable.

Test seams decrease readability of your productive code and thus might be regarded as pollution of the source code. One however needs to consider if a need of additional parameters introduces not even more noise.

Upvotes: 0

Sandra Rossi
Sandra Rossi

Reputation: 13646

I half agree with the answers given by Haojie: one should not use Test Seams (exist since ABAP 7.50) for a so simple case (replacing sy-uname), you should only use a provider class as he proposed.

Test seams are considered like a pollution of the productive code, because it reduces the readability of the code (mix of productive and test code).

NB: the ABAP documentation of test seams (link above) gives at least the following possible usages:

  • Authorization checks (AUTHORITY-CHECK)
  • ABAP SQL statements (SELECT, MODIFY, etc.) - It became a bad example since ABAP SQL can be mocked with the ABAP 7.52 class CL_OSQL_TEST_ENVIRONMENT.

As a rule-of-thumb, test seams should not be used at all, or considered as a solution of very last resort.

BUT if there's no other choice, like adding tests to a "legacy" code (old-badly-written code, usually not written using object-oriented design patterns, that is considered not testable via ABAP Unit), then you may have eventually no other choice.

As stated by Horst Keller (one of best ABAP experts in the world and responsible of ABAP documentation at SAP):

"If you cannot redesign and rewrite the whole application, as a workaround you make the code test dependent. This is regarded as bad style, but it helps."

As the question is only about sy-uname and it's not about the whole program, so the effort to refactor sy-uname is much lesser, so there's no excuse to not using a class.

Upvotes: 5

Haojie
Haojie

Reputation: 5713

It is not possible to mock system parameters as far as i know. there are two ways to achieve this.

1.TEST-SEAM/TEST-INJECTION

TEST-SEAM sy_uname.
   DATA(lv_uname) = sy-uname.
END-TEST-SEAM.  
"other code 

Your unit test:

TEST-INJECTION y_uname.
   lv_name = "my_mock_user".
END-TEST-INJECTION.

2.Test Double

Define a interface

INTERFACE if_user

  METHODS get_uname
    RETURNING 
     VALUE(rv_uname) TYYPE syst_uname.

ENDINTERFACE.

In your production code, you create a CLASS to implement this interface and in the get_uname to return sy-uname.

somewhere in your code, you need to provide a SET method to be able to set the instance of IF_USER like below, and in the production code you call the instance of if_user~get_uname to get the user name.

METHODS set_user_provider
  IMPORTING 
    !io_user_provider TYPE REF TO if_user.

In your unit test code, you create a local CLASS to implement this interface and in the get_uname to return your mock user.

CLASS lcl_mock_user_provider DEFINITION FOR TESTING. 
   PUBLIC SECTION.
     INTERFACES if_user.
ENDCLASS. 

CLASS lcl_mock_user_provider IMPLEMENTATION.
   METHOD if_user~get_uname.
     "return your mock user name. 
   ENDMETHOD. 
ENDCLASS.

your unit test code:

DATA(lo_mock_user_provider) = NEW lcl_mock_user_provider( ).

MyClassInstance.set_user_provider( lo_mock_user_provider ). 

Upvotes: 2

Related Questions