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!
As background I have been writing a blog about each of the weekly elements of this course.
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?
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:-
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.
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
everything I have done thus far could be described as a multi-level test.
- 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:-
- “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?