IoC Container in ABAP Part II

Introduction

In the first part of this series I introduced a file backend which supports multiple types of storage types and allows the combination with authorization check or file filter classes.

The design pattern in use is the decorator pattern. Every class which implements additional functionality declares the usage of a member instance which is described by the same interface which is already implemented by the class itself.

This allows us to have more flexibility than in another approach which used inheritance.

Having a decorator based file access backend and a factory which creates and combines these instances for you comes close to what the requirements said. But already the very first requirement – “Multiple storages will need to be supported by configuration, e.g. cluster database storage or SAP Generic Objects Services based storage” is not yet satisfied: There is no possibility to configure the object hierarchy yet.

In this blog post I’m going to show you how this can be achieved by utilizing an IoC Container.

Configuring the IoC Container by API

The decorator pattern allows us to have the flexibility we need. The instances may be combined without any restriction since they implement the same interface. But this approach is still a static one, because of the way the objects are created.

If this coding was part of a factory, you would need to have a new method every time you request a slightly new combination of object instances.

In software which is to be shipped to customers, having a factory which is not extensible would be too restrictive.

The coding which creates and combines all the instances could also be implemented by a BADI. The advantage would be that every customer could implement its own file backend by combining certain instances.

Unfortunately, in the first part of this series, one requirement stated clearly that there could be multiple scenarios in which the file access could be used: “within the same system, there could be multiple scenarios, which require different kinds of storage types”

So what would be the solution? Have a BADI implementation for each of these scenarios?

With an IoC Container you could define freely combined object hierarchies. This definition could be done in source code but could also be done in customizing.

The basis of the configuration of an IoC Container is the contract. The contract is, technically spoken, an ABAP OO Interface.

There could be one or multiple implementers for each contract. Implementers are, technically spoken, ABAP OO classes which implement this interface.

The easiest example which shows the usage of an IoC Container is the creation of one class for one interface:

The IoC Container is created using the following call:

DATA lo_ioc_container TYPE REF TO /leos/if_ioc_container.

*       create IoC container

lo_ioc_container = /leos/cl_ioc_container=>create_empty_instance( ).

Next, a contract and its implementer need to be registered:


*       register instance for binary access

lo_ioc_container->add_implementer(

EXPORTING

iv_contract = '/leos/if_ioc_demo_bin_access'

iv_implementer =  '/leos/cl_ioc_demo_bin_access' ).

The instance can be resolved with the GET_IMPLEMENTER-method of the IoC Container.

This example does not have any benefit yet. The implementation is not yet defined in the customizing and we didn’t even combine two instances with each other. The combination comes next.

If there are multiple implementers for one contract, they can be distinguished by using a filter value.

This filter value can be handed over when the GET_IMPLEMENTER-Method is called.

Since we need to combine two instances directly with each other, we use another basic concept of the IoC Container: Configuration of Constructor Injection. In the example the internal table LT_CI takes care of the constructor injection

Requesting the implementer still works the same way we’ve already seen before.

The whole code looks like this:

The debugger shows how this object is nicely nested:

The IoC Container will resolve all dependencies, which are declared by constructor injection, automatically for you.

If the implementer of the dependency declares its own dependencies, they are of course also resolved before the object is getting created – which means that you can nest dependencies as deep as you want.

Features like lifetime control, Setter-Injection, passing parameters by the application or passing default values to a constructor are also included.

Customizing

Up to this point, there was still a drawback: We defined the implementers directly in the coding at design time. Manually nesting these implementations would still be easier. So let’s shift all the setup routines of the previously shown coding to the customizing.

The whole definition starts with an application. It’s up to you which name you chose.

Now let’s declare the contracts. There is only one in this application. Since we want the IoC Container to create a new instance every time we ask for it, we set the lifetime to “transient”

There are two implementers yet, one is the default implementer, while the other one is only valid if a filter value ‘BA’ has been passed by the application or has been declared in the constructor injection configuration.

The constructor injection configuration of /LEOS/CL_IOC_DEMO_ACCESS_AUTH comes next:

That’s it – we are done.

The whole coding to get an instance can be simplified to this piece of code:

Conclusion

The setup of complex object hierarchies can be dramatically simplified with the help of an IoC Container. The container takes care of the creation of the objects you need, satisfies all required dependencies and controls the lifetimes. Changes to the hierarchy are possible by only changing the customizing. The code is never again touched for this type of change.

Advertisements

IoC Container in ABAP Part I

Introduction

This is the first part of my new series “IoC Container in ABAP”.

“IoC” is an abbreviation of “Inversion of Control” which is a programming paradigm. Instead of describing it in endless boring chapters I’m going to give you an example today.

Today I’m going to give you an introduction in the scenario which we are going to deal with through the upcoming blog posts.

Bad news is: There will be no IoC Container shown today.

Good News is: I’m going to prepare an application architecture which will match perfectly to what an IoC Container should usually automate. There will be bad architecture and there will be a not so bad one.

Bad news II is, that this example is not really an example which shows the full power of an IoC Container. In fact, for an IoC Container it is a very tiny example which could also be realized without such a container. However this small example illustrates how dependencies work and should be used. And that is what an IoC is about in the first step (beside many other aspects).

Imagine, a new file access component would have to be designed in pure ABAP, having the following requirements

  • Multiple storages will need to be supported by configuration, e.g. cluster database storage or SAP Generic Objects Services based storage
  • The client of the component should not be aware of the implementation details, hence, should not need to know which implementation is chosen
  • Having an extensible backend, which supports the following features:
    • authorization checks when saving the files
    • authorization checks when accessing the files
    • file filters
    • multiple storage types (as mentioned above)
    • the above mentioned features need to be configurable based on the usage scenario
  • within the same system, there could be multiple scenarios, which require different kinds of storage types

This blog post is not going to deal with the implementation details of these features – it is going to deal with the architecture which allows us to support the kind of flexibility we require.

Interfaces

So what to do? Go and create a new file backend class which performs hard coded authorization checks on its own authorization object? Or which filters files based on some customizing? Check customizing and switch to sub-methods using IF- or CASE -Statements?

No – let’s start with some interface descriptions in order to get an idea of what a file backend should provide. Let’s do not take care of any implementation details.

In this demo it is going to be a pretty simple interface description. The only operations which will be supported are saving and retrieving file contents. There will be no file lists. And there will be no directories, not in this demo.

In fact, this is the only interface which will be shown today.

Drawbacks of having one class

If the required functionality was only implemented by one class, there would be certain drawbacks:

  • Authorization checks would have to be implemented in the same class which also deals with storing files to the backend. Having more than one responsibility for one class is a clear violation of the Single Responsibility Principle
  • Same for the file filter – it would be again a violation of the Single Responsibility Principle
  • Additional functionalities would not be testable in an isolated environment
  • Customer specific changes, e.g. in the way files are stored in the backend would require to change the existing class. This is a violation of the Open Closed Principle.
  • If the file access service would be used in various scenarios, a certain kind of file access would be chosen every time. This violates the principle of having loosely coupled components

Realization Variant 1 – Inheritance

The file access is described by the interface /LEOS/IF_IOC_DEMO_BIN_ACCESS.

The classic approach is to have classes which inherit from each other. The authorization check would inherit from the file access class. Every time a method is called, the authorization check class performs its checks before the super class method is called. Same for the file filter. Any client uses in fact not the file access, but rather one of its sub classes.

Unfortunately this approach has some severe drawbacks. If the file access needs to be replaced by another implementation which access a different kind of file repository, the whole inheritance hierarchy would have to be created again, causing the creation of two classes for each file filter and authorization check, but having the same responsibility.

The written classes are not reusable since they are caught in an inheritance hierarchy. Further requirements would result in further child classes.

And any super class functionality that exists in between cannot just be switched off if the use case or the customer requirements ask for it.

So in terms of maintenance and extensibility, features that have been explicitly written down at the top of this blog, this approach is a bad one.

Realization Variant 2 – Decorator

So instead of having big inheritance hierarchies, we use the decorator pattern instead. Every new class implements  the interface /LEOS/IF_IOC_DEMO_BIN_ACCESS on its own.

Every class which implements additional functionality, like authorization checks or file filters, has a member instance MO_BIN_ATT_ACCESS type with the same Interface /LEOS/IF_IOC_DEMO_BIN_ACCESS.

Validations or checks can be done in the specific method of the specific class which is responsible for the implementation of the feature, and if certain checks fail, they are not going to be delegated to the member instance.

The classes declare the dependencies by their constructors. The classes /LEOS/CL_IOC_DEMO_ACCESS_AUTH and /LEOS/CL_IOC_DEMO_FILTER do so by expecting another instance of type /LEOS/IF_IOC_DEMO_BIN_ACCESS in their constructor.

This means, any additional functionality and any file access class are freely combinable, since all of them implement the same interface and request a nested instance in their constructor.

This means, any additional functionality and any file access class are freely combinable, since all of them implement the same interface and requested a nested instance in their constructor.

A client may create the file backend by calling a factory which creates and combines the instances.

This piece of code shows the creation of a file access instance and combines it with an authorization check instance. In any upcoming example the file filter is not shown since its existence makes no difference from the architectural point of view. But the examples are getting smaller, hence better to understand.

Having a decorator based file access backend and a factory which creates and combines these instances for you comes close to what the requirements said. But already the very first requirement – “Multiple storages will need to be supported by configuration, e.g. cluster database storage or SAP Generic Objects Services based storage” is not yet satisfied: There is no possibility to configure the object hierarchy yet.

In my next blog post I’m going to show you how this can be achieved by utilizing an IoC Container.

Unit Tests in ABAP using a mocking framework

Introduction

Unit Tests are an integral part of the software development lifecycle. The purpose of tests is to ensure the proper functionality of a system. Also, if changes are made to specific parts of the system, Unit Tests can immediately show if any functionality has been broken during the last update.

A common problem in unit testing is to separate the system under test, which is subject to the unit test, from other parts which are not to be tested. Common parts, which need to be separated and replaced specifically for unit tests are usually business object repositories or database access components as well as components which rely on third party systems.

This blog is not about unit testing in ABAP. This blog is about developing them faster.

Scenario

For demo purposes, I build up a scenario. It includes a simple flight observer, which has access to a flight information system.

Furthermore, it has access to an alert component which raises mail, SMS etc. alerts.

Both components are dependencies on which the observer relies on. Both components will need to be replaced by mockups for testing purpose since they are not subject to the unit test. Rather, the flight observer is to be tested, in the following sections referred to as the system under test (SUT).

The SUT is implemented by /LEOS/CL_FLIGHT_OBSERVER.

The flight information component is described by the interface /LEOS/IF_IS_IN_TIME_INFO.

The alert processor is described by the interface /LEOS/IF_FLIGHT_ALERT_PROCESS.

UML

The validation logic is very simple: It decides, weather an alert for a specific flight is raised or not. Every delay with more than 60 minutes is getting raised. However, in any other real scenario, there wouldn’t be such a simple hard coded routine but for demo purposes it is fine.
All dependencies to the two other components are set via its constructor using dependency injection.

Unit Test realization Part I

The method which is going to be tested is described below and implements the 60-minutes rule: every delay of more than 60 minutes is getting delegated to the alert system.

This implementation is not a challenge at all. More tedious is the implementation of local classes which are implementing the functionality of the two components on which the SUT depends. These implementations are called mocks.

The flight information access, implementing /LEOS/IF_IS_IN_TIME_INFO, would have to return flights which are sometimes in time and sometimes not. This flight information would have to be hard coded in the unit test. It might look like this:

There would be another mockup for the alert component, tracking the number of calls made to the method ALERT_DELAY( ). We do not show it here.

The setup routine for the unit test to setup all the required objects would look like this:

The test now calls the SUT with various flight data. Some data will lead to a delay, like flight LH / 402 / in November 9th 2012, some will not (like LH / 402 in November 10th 2012). The test is going to evaluate if the alert processor has been called or not, depending on the requested flight data.

Unit Test realization Part II

The described approach requires  a lot of coding to implement the mockups. This coding is never ever reused and basically a waste of time.

This is where mocking frameworks come into place.

Mocking Frameworks are not meant for productive usage, however, their only usage is to save coding in unit tests, hence, development efforts.

The same mockup, which returns flight information data could be mocked using a mocking framework:

The whole mockup implementation is described by only two lines of ABAP coding. Three more lines include data declarations and one line actually generates the mock object for you. This sums up to 8 lines of code, in comparison to 14 lines of code which are needed for the manual implementation.

The mockup for the alert processor is even more simple, since its only purpose is to track the number of calls made against method ALERT_DELAY. This feature is already included in the mocking framework as described in the test coding utilizing these mocks. You would just need to call method HAS_METHOD_BEEN_CALLED( … ) on the corresponding mocker object.

Unit Test Coding

REPORT /leos/_test_flight_observer.

*----------------------------------------------------------------------*
* CLASS lcl_test_observer DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_test_observer DEFINITION FOR TESTING.
"#AU Risk_Level Harmless
"#AU Duration Short
PROTECTED SECTION.

DATA mo_is_in_time_access TYPE REF TO /leos/if_is_in_time_info .
DATA mo_is_in_time_mocker TYPE REF TO /leos/if_mocker.
DATA mo_alert_processor TYPE REF TO /leos/if_flight_alert_process .
DATA mo_alert_processor_mocker TYPE REF TO /leos/if_mocker.

DATA mo_system_under_test TYPE REF TO /leos/cl_flight_observer.

PRIVATE SECTION.
METHODS setup.
METHODS teardown.
METHODS test_no_alert FOR TESTING.
METHODS test_with_alert FOR TESTING.
ENDCLASS. "lcl_test_observer DEFINITION

*----------------------------------------------------------------------*
* CLASS lcl_test_observer IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_test_observer IMPLEMENTATION.
METHOD setup.
DATA lo_mocker_method TYPE REF TO /leos/if_mocker_method.
* create the flight information backend
mo_is_in_time_mocker = /leos/cl_mocker=>/leos/if_mocker~mock( iv_interface = '/LEOS/IF_IS_IN_TIME_INFO' ).
lo_mocker_method = mo_is_in_time_mocker->method( 'GET_DELAY' ).
lo_mocker_method->with( i_p1 = 'LH' i_p2 = 402 i_p3 = '20121109' )->returns( 100 ).
lo_mocker_method->with( i_p1 = 'LH' i_p2 = 402 i_p3 = '20121110' )->returns( 5 ).
* this call creates the flight information mockup
mo_is_in_time_access ?= mo_is_in_time_mocker->generate_mockup( ).

* create an empty alert backend (we just need to track the number of method calls)
mo_alert_processor_mocker = /leos/cl_mocker=>/leos/if_mocker~mock( iv_interface = '/LEOS/IF_FLIGHT_ALERT_PROCESS' ).
* this call creates the alert processor mockup
mo_alert_processor ?= mo_alert_processor_mocker->generate_mockup( ).

* create the flight observer which is subject to this test
CREATE OBJECT mo_system_under_test
EXPORTING
io_alert_processor = mo_alert_processor
io_in_time_access = mo_is_in_time_access.
ENDMETHOD. "setup
METHOD teardown.
ENDMETHOD. "teardown
METHOD test_no_alert.
mo_system_under_test->observe_flight( iv_carrid = 'LH' iv_connid = 402 iv_fldate = '20121110' ).
cl_aunit_assert=>assert_initial( mo_alert_processor_mocker->has_method_been_called( 'ALERT_DELAY' ) ).
ENDMETHOD. "test_no_alert
METHOD test_with_alert.
mo_system_under_test->observe_flight( iv_carrid = 'LH' iv_connid = 402 iv_fldate = '20121109' ).
cl_aunit_assert=>assert_not_initial( mo_alert_processor_mocker->has_method_been_called( 'ALERT_DELAY' ) ).
ENDMETHOD. "test_with_alert
ENDCLASS. "lcl_test_observer IMPLEMENTATION

Conclusion

This was only a simple example. But even this example showed how easy about 40% of code required to mock objects could be saved. What about real world scenarios with even more complex dependencies and behaviour? In fact, 40% of coding can be saved also in these projects, often even more. Most objects can be easily mocked using a mocking framework if they are really treated as a black box.

What a good mocking framework should also provide, is mocking of specific classes using inheritance. Also, exceptions might need to be registered for specific inputs.

Currently, the described mocking framework is still under development. It supports mocks based Interfaces, method call observation and raising of exceptions but not yet the mocking based on already existing classes. However, it is in productive usage in our projects and has already been extremely valuable for making unit tests simpler and easier to implement.

Further blogs may focus on how to manage dependencies in big development projects and how dependency injection might work in ABAP using an IoC Container. Stay tuned.

Introduction

I’m an SAP consultant working for leogistics GmbH which is located in Hamburg. I started as a student in a dual study program at SAP AG in Walldorf. After my studies I worked for two more years at SAP in the Value Prototyping.

In this blog I’m going to write about several architectural topics dealing with the question how certain requirements can be implemented with the tools SAP provides with it’s Netweaver on the ABAP stack.