Continuous Integration has been the norm for many software companies for over 10 years now. The debate about whether the approach is sound is largely done and the empirical data is in…. Continuous Integration improves software quality and reduces the cost to create software products.
So why hasn’t it caught on in SAP?
CI may have had an impact on the edges of SAP i.e. Java apps integrating into ECC or HTML5 apps making REST calls into ECC may be covered by unit test automation, but why isn’t the core?
ABAP supports unit testing…. so where’s all the test automation?
I’ve been told that it can’t be done in SAP or that the benefits don’t stack up.
The facts are though that it can be done and the benefits do stack up, this is why I’m writing this blog. I want to share how my organisation tackles Continuous Integration for SAP and why you should be adopting these practices also.
First Up…. what’s Continuous Integration?
Continuous Integration (CI) is a development methodology that requires developers to integrate their code changes into a central repository frequently. Each change is verified by an automated build and unit tests (possibly even automated integration and regression tests). See https://www.thoughtworks.com/continuous-integration for a good definition.
I know that these things don’t exist in SAP, but we can achieve the same sort of results in a slightly different way. In fact in my humble opinion SAP offers a way to deliver software changes faster and safer than the non-SAP world.
Sold… I’m all in on CI!
Yep, we absolutely need Continuous Integration, but first we need to understand what a unit test is. When you understand unit tests you start to develop software differently, you develop software in a better way.
What is a Unit Test?
A unit is the smallest part of an application, and a test is a test! Unit tests demonstrate the behaviour of units. They don’t prove that units work, they increase our confidence that the units work. Units need to be sized appropriately so that we don’t spend the rest of our natural lives writing complex test cases.
Unit Tests are Independent
Unit tests are independent, they are repeatable and they are not dependent on external factors e.g. a test database with lots of test data that any old Joe Smoe Bag Oh Doughnuts can change.
Actually it’s even more fundamental than this. When you start to think about how you will test your units at design time I guarantee you that you will change the way that you write code.
This is why the Test Driven Development (TDD) guys and girls say that TDD is a way of designing software. TDD is a methodology for designing software, the test automation bit is a side effect.
Unit tests are Fast
When unit tests are independent and your units are sized appropriately your tests should be fast. Fast is good because it means we can give development fast feedback.
Here’s the rub though….
The biggest challenge though in my humble opinion is accepting that you need to design your software in a different way. If you find that it’s difficult to write tests for your code the culprit is probably not the unit tests but more likely your application code.
I know acronyms in software are often contrived, but….
I like the SOLID acronym, personally I found this very useful when trying to structure my code in a way that makes unit testing less painful (https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)). The acronym is slightly contrived but the principles behind it are valuable.
S is for Segregation of Responsibility
All we need to do here is make sure our classes do one thing and one thing only. If we create complex classes that do lot’s of different things guess what…. they’re hard to test. S is for structure, S is for simplicity.
O is for “Open / Closed Principle”
The bottom line here is that you should be able to extend your classes without having to pop the lid open. By far the most common example of this principle is the area calculator class, a quick google search will generate pages of blogs around this example.
Here are the basics: instead of modifying a class that does not quite fit your needs, we should declare a new class that inherits the features of the original class and adds new features. If you find that you’re inheriting methods or properties that are not relevant to your implementation you probably need to rethink your class structure.
L is for Liskov Substitution Principle
Ok, stick with me, it’s nowhere near as complicated as it sounds. And actually this where the mnemonic is being stretched, because really this principle is an extension of the Open Closed Principle.
The thing to remember here is that when we create class hierarchies we should extend classes by creating derived classes, but we should take care not to replace the functionality of the base class. It leads to confusion around the purpose of the class and probably means that the problem we’re solving has not been understood fully.
I is for Interface Segregation
This principle basically tells us that classes should not be forced to rely on interfaces that they don’t need, so in this sense it’s better to have many specific interfaces than it is to have one large interface.
Here’s a good justification for this approach: http://www.oodesign.com/interface-segregation-principle.html
D is for Dependency Injection
Dependency Injection (DI) is a fundamental in my view. DI (i.e. pass a dependency in a parameter to a constructor) is a strategy for decoupling classes, it’s very difficult to make unit tests independent and repeatable without DI.
DI tells us to inject dependencies like database connections into classes, if we can achieve this we can carefully control the domain that our tests are executing in.
For example if we inject a database connection object we can choose to send the real object or a mocked object, where the mock is an object that mimics the behaviour of the real object in a carefully controlled way. This is how we make our tests repeatable.
There are basically three patterns of dependency inversion. I won’t go into them here, but here’s a good link that covers the ground: http://www.martinfowler.com/articles/injection.html
It’s not all sunshine and flowers though…
One slightly distasteful side of effect of DI is the effort required to instantiate objects with nested classes goes up. If you’re managing dependencies several layers deep they still need to be injected, so you may end up with several lines of object instantiation code before you can create the object you really want to use. Tedious is a word that springs to mind.
This problem has been solved and there are a tonne of open source container projects for a variety of languages, although none for ABAP. At my current organisation we rolled our own container, which we may well make open source if there’s enough interest.
A container is simply an object that knows how to instantiate and configure objects. And to be able to be successful it needs to knows about the constructor arguments and the relationships between the objects. Instead of creating objects yourself, you ask the container to create them for you.
If you can come to terms with the impact of SOLID and the need for containers you should be able to start creating unit tests.
Bear in mind that writing good unit tests is almost as challenging as writing the code itself, if you find your tests are becoming too complicated this probably means that your classes are too complicated (notwithstanding that some things are complicated because they are complicated).
Continuous Integration Servers
The next problem to overcome is how to ensure your tests are run frequently. There are a handful of posts within SCN about using Jenkins (formerly Hudson) to drive CI, but they’re not really addressing the issue of CI for ABAP.
The challenge for the off the shelf CI servers is that they don’t integrate with SAPs change mechanism elegantly or at all! The concept of a “Broken Build” doesn’t apply in quite the same way to SAP because we ship micro changes (i.e. small transports).
Micro changes are the reason why SAP can can deliver changes quickly in a way fully compatible with Agile.
In my organisation we have a created a tool that integrates into ABAP Unit for ensuring that unit tests are green before a transport is shipped. We can attach rules around code coverage to prevent code with limited test coverage being shipped automatically i.e. it can still be shipped but it has to be a conscious decision.
There is nothing in the SAP technology stack to prevent ABAP developers from adopting unit testing & CI practices that are common and well proven from the non-SAP / ABAP world. The rise in prominence of the digital economy is forcing us all to think about we can achieve a high cadence in software delivery, but in a way that does not short change product quality.
Continuous Integration and Unit Testing are foundational components of any shift towards supporting frequent safe releases into production.