Continuous Integration in ABAP
Continuously testing your ABAP codes with ADT APIs, Postman, and Jenkins
What is Continuous Integration (CI)?
Continuous Integration (CI) is a development practice that requires developers to integrate code into a shared repository several times a day. Each check-in is then verified by an automated build, allowing teams to detect problems early.
By integrating regularly, you can detect errors quickly, and locate them more easily.
What Does This Mean for ABAP?
Since ABAP codes always reside on the centralized server so codes integration may not be an issue for ABAPer. They always check-in when they activate their codes, that’s when the new codes get verified and available to others to see and execute.
So CI in ABAP is more about continuously testing your activated codes to make sure it is working and as well as other codes. To get more confident on this, you may want to run unit tests, check for code coverage, and run Code Inspector to ensure it is in a good shape and ready to deploy.
However, activating ABAP codes may be different from checking-in the codes in other languages. ABAPer always activate their codes so they can execute (faster) and move on to the next dependent object. So activating ABAP codes does not usually mean the codes are ready to test.
In other languages, developers usually check in their codes (to Git, for example) when they are ready to test. They may not want their codes to break down the application when the codes get integrated and the CI is triggered.
Triggering CI every time ABAPer activating their codes may be excessive. So separating check-in from activating sounds more appropriated.
Checking In ABAP Codes to Git
ABAP codes are kept and version-controlled on the server already so why we need to dual-maintain them in one more place. This is the question I have at the beginning but later I found out that pushing ABAP codes to Git has some extra benefits:
- We can separate the check-in from activating. ABAPer can always activate their codes in the same way they have been doing for decades. But they only check in their codes when they want to commit that their codes are ready to test. Exactly the same as what developers do in other languages.
- We can trigger the CI pipeline in the same way we do for other languages. We know Git is a trigger point of most of CI pipelines and tools. When developers push their codes, they should be checked and verified.
- Git version control is much better than ABAP. In ABAP, the version control happens at the file level and it usually creates a new version after it is released. Sometime the development takes for months and your code changes never get tracked until they are released unless you do it manually for each of them.
The tool like abapGit is your best friend.
Exploring ADT APIs
Several years ago, SAP released ABAP Development Tool (ADT) or ABAP in Eclipse (AiE) so you can coding ABAP right in Eclipse using their plug-ins. Not just the Eclipse plug-ins they released but also the RESTful APIs on the ABAP back-end to allow ADT to work.
These APIs are standard RESTful APIs so you can reuse them from anywhere. SAP also released a good tutorial document on how customer customer can develop their own Eclipse plugin using these APIs or even create yours. (You can also refer to this tutorial blog.)
The open-source project like SAPlink is also leveraging these functionalities. (See this thread for more information)
Note: By using these functionalities, you still need to comply with SAP license terms and conditions.
ABAP Communication Log
In ADT, there is a view called ABAP Communication Log which you can monitor the API communication between ADT and ABAP backend.
Here is what you will see when running ABAP Unit:
You can double click each message to see all the detail of HTTP message.
Here you can explore how ADT communicate with ABAP backend for each action. So anything you can do in ADT, you can also do it with REST APIs and automate them.
API Automation with Postman
Postman is a popular tool developers use to test APIs. It is a Google Chrome App you can install from Chrome Web Store. In addition to API testing, Postman allows you to write test scripts to automate the workflow.
Writing script is beyond of this blog but you can check out some example from this page. It is basically a JavaScript so you don’t need to learn new language (Assuming JavaScript is a common language for every developers these days).
Writing Test Script
Since the ADT APIs are XML-based so you need to convert the result into JSON first so you can process within Postman.
Design Your CI Pipeline
The pipeline can be different for each team. Here is mine:
This blog, I will show you how to automate ABAP Unit, Code Coverage and SCI using Postman. (newman is the command line tool of Postman)
Implementing Your Scripts
If you don’t want to start from scratch, I’ve already written scripts and share on GitHub. Check out the README for more information and how to config the scripts for your environment.
Testing Your Script
Note: I’ve tested these scripts with my SAP Developer Edition Instance (NPL). You can get one by referring to my blog here.
Exporting Your Script
Export your test scripts and environment parameters and you will get JSON files. (See to my files on GitHub as examples)
Integrating Postman Scripts into Jenkins
You may transfer the JSON files to your Jenkins server. But I prefer to maintain those files on Git and let the Jenkins pulls from there. (Test scripts are also codes which you may need to version control!)
Pull code from Git
Create your first job to pull ABAP codes from Git repository. Here, I use my ABAP REST API repository as an example. (See my blog about it here)
Set it to poll Git every minute, for example.
Run ABAP Unit and Code Coverage
Setup another job to run newman. You may need to first change directory to workspace directory or anywhere on the server that you keep your test scripts.
Here is a sample output:
C:\Program Files (x86)\Jenkins\workspace\ABAP Unit Coverage>cd C:\Users\administrator\Documents\Git\abap-ci-postmanC:\Users\administrator\Documents\Git\abap-ci-postman>call newman run abap_unit_coverage.postman_collection.json --environment NPL.postman_environment.json
newmanabap_unit_coverage→ GetToken
┌
│ '==========Environment Variables=========='
│ 'protocol=http'
│ 'host=vhcalnplci.dummy.nodomain'
│ 'port=8000'
│ 'authorization=Basic xxx'
│ 'package=$REST'
│ 'coverage_type=statement'
│ 'coverage_min=80'
│ 'coverage_maxlevel=2'
│ 'coverage_chklevel=0'
│ 'exclusion=[{"name":"ZCL_COMPLEX_RESPONSE==========CP","type":"CLAS/O
│ CI","reason":"Data model class"}]'
│ 'atc_variant=DEFAULT'
│ 'x-csrf-token=YE9m7YEdPAEI7Zcj2KPn8Q=='
└
GET http://vhcalnplci.dummy.nodomain:8000/sap/bc/adt/abapunit [404 Not Found,843B, 106ms]
√ X-CSRF-Token is present→ RunABAPUnit
POST http://vhcalnplci.dummy.nodomain:8000/sap/bc/adt/abapunit/testruns [200 OK, 2.71KB, 2.4s]
√ Status code is 200
√ Job finished successfully
√ No critical alert for $REST/CLAS/OC/ZCL_REST_HANDLER [Actual=0]
√ No critical alert for $REST/CLAS/OC/ZCL_REST_RESOURCE [Actual=0]
√ Coverage ID 000D3AA2B4DA1EE7A5C18580FC24BF6F→ CheckCodeCoverage
POST http://vhcalnplci.dummy.nodomain:8000/sap/bc/adt/runtime/traces/coverage/
measurements/000D3AA2B4DA1EE7A5C18580FC24BF6F [200 OK, 5.57KB, 116ms]
√ Status code is 200
√ >>CLAS/OCI/ZCL_COMPLEX_RESPONSE==========CP statement coverage = NA% [Exclu
ded: Data model class]
√ >>>>CLAS/OCI/ZCL_REST_HANDLER statement coverage = 100.00%
√ >>CLAS/OCI/ZCL_REST_HANDLER==============CP statement coverage = 100.00%
√ >>>>CLAS/OCI/ZCL_REST_RESOURCE statement coverage = 100.00%
√ >>CLAS/OCI/ZCL_REST_RESOURCE=============CP statement coverage = 100.00%
√ DEVC/K/$REST statement coverage >= 80% [Actual=100.00%]┌─────────────────────────┬──────────┬──────────┐
│ │ executed │ failed │
├─────────────────────────┼──────────┼──────────┤
│ iterations │ 1 │ 0 │
├─────────────────────────┼──────────┼──────────┤
│ requests │ 3 │ 0 │
├─────────────────────────┼──────────┼──────────┤
│ test-scripts │ 3 │ 0 │
├─────────────────────────┼──────────┼──────────┤
│ prerequest-scripts │ 1 │ 0 │
├─────────────────────────┼──────────┼──────────┤
│ assertions │ 13 │ 0 │
├─────────────────────────┴──────────┴──────────┤
│ total run duration: 3.1s │
├───────────────────────────────────────────────┤
│ total data received: 8.14KB (approx) │
├───────────────────────────────────────────────┤
│ average response time: 869ms │
└───────────────────────────────────────────────┘C:\Users\administrator\Documents\Git\abap-ci-postman>exit 0
Finished: SUCCESS
Run Code Inspector (SCI)
Setup another job to run ATC.
C:\Program Files (x86)\Jenkins\workspace\ABAP Code Inspector>cd C:\Users\administrator\Documents\Git\abap-ci-postmanC:\Users\administrator\Documents\Git\abap-ci-postman>call newman run abap_sci.postman_collection.json --environment NPL.postman_environment.json
newmanabap_sci→ GetToken
┌
│ '==========Environment Variables=========='
│ 'protocol=http'
│ 'host=vhcalnplci.dummy.nodomain'
│ 'port=8000'
│ 'authorization=Basic xxx'
│ 'package=$REST'
│ 'coverage_type=statement'
│ 'coverage_min=80'
│ 'coverage_maxlevel=2'
│ 'coverage_chklevel=0'
│ 'exclusion=[{"name":"ZCL_COMPLEX_RESPONSE==========CP","type":"CLAS/O
│ CI","reason":"Data model class"}]'
│ 'atc_variant=DEFAULT'
│ 'x-csrf-token=YE9m7YEdPAEI7Zcj2KPn8Q=='
└
GET http://vhcalnplci.dummy.nodomain:8000/sap/bc/adt/abapunit [404 Not Found,835B, 125ms]
√ X-CSRF-Token is present→ CreateWorklist
POST http://vhcalnplci.dummy.nodomain:8000/sap/bc/adt/atc/worklists?checkVariant=DEFAULT [200 OK, 182B, 47ms]
√ Status code is 200
√ Worklist ID 000D3AA2B4DA1EE7A5C19C55F9953F86→ RunATC
POST http://vhcalnplci.dummy.nodomain:8000/sap/bc/adt/atc/runs?worklistId=000D3AA2B4DA1EE7A5C19C55F9953F86 [200 OK, 601B, 2.6s]
√ Status code is 200
√ No error found [Actual=0,0,7]┌─────────────────────────┬──────────┬──────────┐
│ │ executed │ failed │
├─────────────────────────┼──────────┼──────────┤
│ iterations │ 1 │ 0 │
├─────────────────────────┼──────────┼──────────┤
│ requests │ 3 │ 0 │
├─────────────────────────┼──────────┼──────────┤
│ test-scripts │ 3 │ 0 │
├─────────────────────────┼──────────┼──────────┤
│ prerequest-scripts │ 1 │ 0 │
├─────────────────────────┼──────────┼──────────┤
│ assertions │ 5 │ 0 │
├─────────────────────────┴──────────┴──────────┤
│ total run duration: 3.4s │
├───────────────────────────────────────────────┤
│ total data received: 573B (approx) │
├───────────────────────────────────────────────┤
│ average response time: 936ms │
└───────────────────────────────────────────────┘C:\Users\administrator\Documents\Git\abap-ci-postman>exit 0
Finished: SUCCESS
Create the Pipeline
Connect those three jobs together to create a pipeline.
You can additionally create a Build Monitor view for continuous monitoring your pipeline status.
Note: Learn more about how to setup Jenkins from my blog.
Now, you’ve got the CI pipeline running. Every time, ABAPer checks in their codes, it triggers the pipeline to test and verify them.
Continuous Integration doesn’t get rid of bugs, but it does make them dramatically easier to find and remove.
— Martin Fowler
Lastly, keep reminding yourself why you need CI?