Skip to Content
Author's profile photo Paul Hardy

SAP Open Course: Unit Testing: Week 5: Dependency Lookup

Unit Testing and TDD? Magic Bullet or Utter Waste of Time? Love it or hate it, this course has got people talking, and each week as the complexity meter approaches “critical” the emotional opinions just get more pronounced.

This week the subject was DEPENDENCY LOOKUP!

Look Up

As background I have been writing a blog about each of the weekly elements of this course.

https://blogs.sap.com/2018/03/18/sap-open-abap-unit-testing-course-week-one/

https://blogs.sap.com/2018/03/24/open-sap-course-unit-testing-week-two-tdd/

https://blogs.sap.com/2018/03/28/open-sap-course-unit-testing-week-three/

https://blogs.sap.com/2018/04/05/open-sap-course-unit-testing-week-four-test-isolation/

I have generally presented an overwhelmingly positive view (apart from about pair programming) whilst there have been some equally negative opinions expressed in other blogs, roughly balancing me out so we remain in a point of equilibrium.

The point is there have been dozens of blogs on this subject since the course began, and before this course, if you did a Google about ABAP and Unit Testing all you would have got was a few crazy sounding blogs by myself.

To mix metaphors, once the genie is out of the bottle, you cannot stuff the cat back into the bag, and a lot of people who would have never even heard of the subject are most likely experimenting with it. They may all decide its nonsense, but at least they had the choice, and as far as I am concerned if we go up from 0.1% to 0.2% adoption that is a wonderful achievement.

So what is this dependency lookup nonsense anyway?

I am not going to tell you just yet, to build up the suspense. That is what Stephen King does, sometimes you have to get half way through the book till you work out what it is about. In some books like “Dreamcatcher” you never work it out.

What I will say is by this point in the course those who were unfamiliar with OO concepts in general would have slit their own throats rather than have to continue listening, as they would have been utterly lost

All the funny terms like “interfaces” and “friends” and “dependency” and “injection” are now flying thick and fast and you have to have a very clear understanding of what went before to make sense of this.

However getting your head around everything that has gone before is a battle worth fighting, as this week’s central idea is fantastic as far as I can see, completely new to me, and I have been researching the subject for years. I started using it at work the very next day – more on that later.

Before we go on, the whole concept is going to make no sense at all unless some technical OO terms are explained at the outset. I asked the IT connection company CISCO the best way to do this and they showed me their website ROUTERGOD where surprisingly knowledgeable celebrities like Paris Hilton explain complicated IT concepts.

I cannot argue with the logic of such an approach so I asked some celebrities to explain the OO concepts of FRIENDS and SINGLETONS.

FRIENDS as explained by Jennifer Aniston

Hello Jennifer Aniston here! To make what goes next clearer I need to explain the concept of a “friend” in OO programming.

Normally a class has, like, you know, private attributes which cannot be accessed by the outside world. However that class can, like, you know, declare “friends” which can access and change those private attributes. This works one way only. The class cannot, like, you know, access the “friends” private attributes unless, like, you know, the, like, you know, friend returns the friendship. I bet at this point some people are saying this is so difficult to understand it must not be their day or month, or even their year.

However it is sort of like, you know, Facebook where you set who can see your photographs. You can set a photograph as “friends only” and your Facebook friends can see that photograph, but you cannot see their photographs set as “only me”.

SINGLETONS as explained by John Noakes and Shep.

Hello John Noakes here! Say “hello” Shep! “Woof Woof”

A singleton in OO programming is when you only want to have one instance of a class. You might think that a static class could do this but – GET DOWN SHEP! GET DOWN – you cannot over-ride static classes which is a bad thing.

In order to earn your Blue Peter Badge what you need to do instead is have a class with all instance methods but private construction The constructor will create a new instance the first time it is called but thereafter – GET DOWN! GET DOWN SHEP! – the original instance is returned so you can never have more than once instance of the class.

Hang on a second, I have just got to climb Nelsons Column. Here I am on the top now and I can tell you that the singleton approach gives you both the benefits of static classes e.g. same class attribute values wherever in your program an instance is created, and also you can subclass the class if desired and SHEP! SHEP! What are you doing to those Lions?

Andy Recap

The central problem with having automated tests in your production code is that traditionally code has been full of “seams” or “dependencies” – tasks such as reading from the database, or printing something out, anything that is not just conditional logic or mathematical calculations. It is only possible to have automated unit tests on the latter, not the former, so you cannot do it if they are all mixed together like a scrambled egg.

Thus far in the course the concepts of “isolating” the seams (e.g. database reads) into their own classes has been covered. The isolated classes can then be replaced by test doubles which are “injected” into the program code, so the code under test is unaware it is dealing with anything other than the real database, and as a result you only test what can be tested (business logic).

Let me put this another way. I want to compare unit testing with HP Quality Centre which runs a test of automated end to end tests of recorded transactions for regression test purposes. At first glance HPQC and ABAP Unit seem to have the same purpose – regression testing, but let us have a compare:-

ABAP Unit vs HPQC Comparison

As can be seen apart from the general concept, the specifics are all different. Most importantly ABAP Unit tests only the business logic and all the other things HPQC tests are replaced with test doubles in the ABAP Unit framework.

You were supposed to be talking about Dependency Lookup?

Yes indeed, so I was. This concept is (to me) a miracle solution to the problems of “injecting” these test doubles into the production code during a test yet leaving the production code utterly unaware tests are ever run upon it.

Let us say you have actually managed to isolate your dependences into their own class e.g. database access class, printing class and so on. That in itself is not such a big deal.

The problem is that you cannot replace a “seam” if an actual real database class (or some such) is instantiated using a CREATE OBJECT statement in the routine under test. I say “routine” as I am very aware that the vast bulk of code out there is procedural, but FORM routines can still have the seams replaced by calls to instances of classes.

So isolation in and of itself is not enough – you need a way to “inject” a test double into the code being tested. In prior weeks we were shown a variety of ways to do this – all of which involves butchering the production code by re-writing it until the cows came home. As might be imagined not everyone was 100% on-board with this idea.

Personally I had always used “constructor injection” up until this point. That seemed like the least amount of butchery needed

Factory Whistle Blows

The alternative to having CREATE OBJECT statements all over the place is to have a “factory” that provides instances of such objects on demand.

I have written about this subject in the following blog:-

https://blogs.sap.com/2017/04/17/charlie-and-the-chocolate-factory-pattern/

The irony is that next week I am actually going to live in an old chocolate factory here in Germany.

So I like having factories instead of CREATE OBJECT as a general thing, independent of unit testing. However as it turns out the two concepts go together like bacon and eggs.

My colleagues and I had always wondered – and this is mentioned in the above blog –if a factory could return a local class instead of a global one base upon whatever logic we deem fit? The answer is a 100% yes as it transpires.

The whole concept is summed up in one slide from the coures instructors:-

SAP Course Slide – Dependency Lookup

There are assorted concepts here working together:-

  • The “dependency” or “seam” objects cannot be created on their own hence “CREATE PRIVATE”. The production code has to get an instance via the factory class. Hence the dependency class has to be a “friend” of the factory class, otherwise the factory class could not create an instance.
  • Since the production code is now forbidden from doing CREATE OBJECT statements for the dependency class, it has to do a call to the factory method.
  • Normally the factory method would use the “singleton” approach described above to make sure the same instance is always returned no matter where the dependency object is required in the production code.
  • The “injector” method can get in there first during the automated unit test run. As the factory has the injector class is a friend, the injector can tell the factory to always return a test double instead of a real class.
  • The factory has a list of objects in can create. The injector has a subset of that list, namely what objects need to be replaced by test doubles, which could in fact be all items on the list.

Up until now I had always put the “factory” method as a static method in the class itself. This way is much better you have one factory class for the application and one injector class for the application.

The comedy duo put it thus – the dependency class is outsourcing its own creation to a factory class which is in turn outsourcing its testing – and only its testing – to the injector class.

NB the Injector class – must be marked FOR TESTING so it cannot execute in production and inject all sorts of evil into the production code for real.

Level 42

The second half of this week’s course was by no means so exciting, though important in its own way. This was called MULTILEVEL TESTS.

I would put it a slightly different way:-

  • You are testing a single routine. You would think this would be normal. You need to isolate the seams in that routine.
  • You are testing a routine which in turn calls of sorts of other routines. You need to isolate the seams in every single routine that could be called.

I would argue that with “Behavioural Driven Development” the latter is by far the most common.

In real life, as documented in this blog

https://blogs.sap.com/2018/04/07/actual-tdd-in-real-life/

everything I have done thus far could be described as a multi-level test.

Random Musings

  • Up until this point all the examples of bad code that needs to be changed is still 100% OO, whereas in real life the vast bulk of ABAP code out there is still procedural
  • There are some unchangeable things like a call to a standard SAP function module, or a proxy to an external system or whatever. In such cases you wrap the unchangeable thing in a “façade” class, which can then be replaced by a test double.
  • ABAP in Eclipse can wrap a function module into a class with the press of a button apparently. That sounds too good to be true, but I am keeping an open mind.

In regard to why wrapping function modules might be a good thing generally look at this blog:-

https://blogs.sap.com/2016/10/29/harlem-function-module-shuffle/

  • “SPIES LIKE US” were back. A “spy” in this case is a test double whose sole purpose is to say that it was called. As mentioned earlier such terminology just gets in the way and puts people off as far as I can see. I say just stick to “Test Double” – that is difficult enough for people to get their heads around in the first place without making it ten times as complicated.
  • There was some laughing by the presenters, always good to see

No RESET please, we’re British

In the examples the injector and the factory class were both 100% static. As was mentioned this violates the idea that the start of each unit test starts off in a pristine state.

That cannot work at all with static classes as the injected doubles from the test before are still there.

The suggested solution was that during SETUP call an injector method called RESET which in turn calls a RESET method in the factory class which blanks out all the static variables.

As I have discovered static variables are the deadly enemy of unit tests. I try to be as “non-static” as I can but sometimes there is no choice. The more I work on this the more I am sure I will be able to square the circle. Any suggestions welcome!

In addition I would LOVE to hear how the dependency lookup worked out for other people. As I said I tried this out the next day for real … maybe I am the only person to so outside of SAP … wo what do you think – did it make things better for me or did I spend the whole day calling the SAP system names?

Cheersy Cheers

Paul

 

 

Assigned Tags

      8 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Mike Pokraka
      Mike Pokraka

      Nice blog, as usual 🙂

      Re. the RESET problem, I tend to use the Abstract Factory Pattern which makes this problem obsolete. Basically the factory itself will always be an instance, usually a singleton.

      So my test might look something like:

      methods setup. 
        zcl_foo_factory=>set_instance( ltd_foo ). 
        zcl_wotsit_factory=>set_instance( lcl_wotsit_automator ).
        ...
      end method.

      On the one hand I find this simpler, but on the other, I kinda like the security aspects presented in the course. But on the other hand, replacing factories has it's place outside a test scope too.

      Then again, what are we achieving by the kind of code security in ABAP?

      It's easy as pie to deliberately circumvent in almost all projects out there; and to accidentally replace a singleton is... well, let's say it's not top of my list of inexperienced developer mistakes.

      Author's profile photo Paul Hardy
      Paul Hardy
      Blog Post Author

      After a big discussion with my colleague who sits opposite me, I turned my factory into a singleton so at the start of each test you get a new one.

      We area always aiming for the "elegant" solution which as I understand it is the simplest most obvious thing that works... in this case the RESET thing seems to be over complicating matters...

      Author's profile photo Andre Schüßler
      Andre Schüßler

      I always use factories as singletons.

      And while testing you can inject a factory double via backdoor injection.

      So the GET_INSTANCE returns a fake factory (doing whatever you want). You don't need a static setter to change the global instance.

      Author's profile photo Mike Pokraka
      Mike Pokraka

      I prefer set_instance for a couple of reasons. There are several legit scenarios outside unit testing where we would want to substitute a factory with a different instance.

      You mention backdoor injection, but then you also say in another comment we should not be using FRIENDS, so am not sure how this would work? I sometimes do this using subclasses, but that’s not always the best approach.

      The injection can still be made private and delegated to a FOR TESTING class if code security is a concern.

      Author's profile photo Andre Schüßler
      Andre Schüßler

      Hi Mike,

      I completely agree with you. The SET_INSTANCE has a lot of other positiv aspects. And I also use it when appropriate.

      But just for testing I often use the backdoor injections, to keep things simple.

      Don't get me wrong with FRIENDS. I don't say you should not use it, but to use other solutions (of the same value) whenever possible.

      In my opinion backdoor Injection is a good example of using LOCAL FRIENDS in good way. Just my two cents.

      Author's profile photo Andre Schüßler
      Andre Schüßler

      Hi Paul,

      what do you think about the injector pattern?

      I dont like how the factory class is granting friendship to the injector class (that is defined with FOR TESTING).

      I think that the production code should know nothing about the test code. Even it is a helper class defined with FOR TESTING.

      The dependencies should only direct from test code to production code. Never in the opposite direction.

      I haven't tried it yet, but what happens if the injector (part of the test code) breaks. Will the factory (production code) survive? Will it compile if the injector does not?

      If it doesn't I would refuse to use this pattern.

       

      Author's profile photo Andre Schüßler
      Andre Schüßler

      p.s. I would also not recommend to use FRIENDS when there is another solution with the same outcome. To me FRIENDS is just bad old stuff from C++, that was introduced as a cheat for old C programmers...

      Author's profile photo Suhas Saha
      Suhas Saha

      Hello Mike PokrakaPaul Hardy

      Tbh the concept of “abstract” factory is basically new to me. If i understand correctly, it adds a layer of abstraction on top of the factory layer.

      What is not clear to me is, who is responsible for “instantiating” the factory? Is it the client or does the factory have a SET_INSTANCE( ), which in turn will be called by the client? From the examples, the factory is instantiated in the client!

      BR Suhas