Wednesday 5 January 2022

Run your ABAP Unit test in CI pipelines

In this blog we will take a look at how to run your ABAP Unit tests in CI pipeline when publishing from AbapGit and how to run the unit tests without relying on additional source code installed on the Abap server.

Why would you even want to consider this approach. You could just keep running your Unit tests like normal.

While this is certainly true, I don’t think you can test too often, so adding another layer of testing in a pipeline where you could also integrate other packages as well is pretty cool. Or possibly you could use this setup to potentially shift your development to a server off the common transport path to later integrate into the test system via a pipeline.

For this example I’ve created a local package $TEST_UNIT2 with a single class in it called ZCL_ABAP_UNIT2

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

In this class I’ve got a simple method called test2

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

And a local test class

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Sidenode: In case you are wondering about about the screenshots

While this obviously isn’t a very elaborate scenario, it does the job for this blog 😉

When I execute this locally in VSCode by pressing CTRL + SHIFT + F11 or using the command palette and typing abapfs ‘run unit tests’ I can then see the test result in the test explorer window

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Now let’s add this to a pipeline.

Firstly on my gitlab server I create a new repository.

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Afterwards we copy the http link to be used in AbapGit

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Now we clone the repository in AbapGit and then we commit and push the changes to the git repository

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Now that we have pushed out code, it’s time to build the pipeline. Click the new file.

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Select the .gitlab-ci.yml template and paste the following in.

image: node:latest

# This folder is cached between builds
# https://docs.gitlab.com/ee/ci/yaml/index.html#cache
cache:
  paths:
    - node_modules/
abap_unit:
  script:
    - npm install -g abap_test_runner_cli
    - abaptr

In the pipeline we will install an NPM package I have created. This will execute the unit tests and give us the output.

Commit the changes afterwards we need to maintain the variables. You can maintain these variables in different ways. This is the simplest. We need a server, package or class, username and password.

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Here I use the package. If you want to use the class, then replace the ABAP_PACKAGE with ABAP_CLASS.

Now let’s save this and try to run our pipeline.

We can see under the Pipeline –> CI that our pipeline has executed correctly

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

And checking the job log it’s passed all unit tests as expected

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Now let’s change the source code to make the test fail.

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

After committing the code to Gitlab via AbapGit we see that our pipeline is now failing

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Checking the details of the job we get the expected fail on the unit test. 1 is actually different to 2. Who knew!

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Alright,not it’s time to get creative. How about we let the pipeline create a gitlab issue directly on a failed test.

Check this out

# This file is a template, and might need editing before it works on your project.
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml

# Official framework image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/node/tags/
image: node:latest

stages:
  - abap_unit
  - create_issue
abap_unit: 
  stage: abap_unit
  script:
    - npm install -g abap_test_runner_cli
    - echo "$( abaptr --skip_image true )" >> test.txt
  artifacts:
    paths:
      - test.txt
create_issue:
  stage: create_issue
  script:
    - apt-get update 
    - apt-get install python3 -y
    - |- 
        if [[ $( grep -q 'Error' test.txt && echo true || echo false ) == true ]]; then
            echo "Creating issue"
            curl --request POST --header "PRIVATE-TOKEN: $private_token" "https://gitlab.agilux.com.au:44388/api/v4/projects/$CI_PROJECT_ID/issues?title=Unit%20test%20failed&description=$(cat test.txt | python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.stdin.read()))")"
            exit 1
        else 
            echo "success"
            exit 0
        fi
  dependencies:
    - abap_unit

Alright let’s go through this step by step.

Stages

The stages section of the yml file is to indicate that the jobs are to be run sequentially and not in parallel

Abap Unit

abap_unit:
  stage: abap_unit <= which stage does the job belong to
  script:
    – npm install -g abap_test_runner_cli
    – echo “$( abaptr –skip_image true )” >> test.txt <= Take the output from the abaptr command and copy into a text file called test.txt
  artifacts: <= Tells the pipeline that new files are to be stored in the context of the pipeline, in this example the text file test.txt
    paths:
      – test.txt

Create_Issue

create_issue:
  stage: create_issue
  script:
    – apt-get update <= Update the package manager as we will install python3. I’m to lazy to find a docker image that has both node and python
    – apt-get install python3 -y <= Install python3
    – |- <= Execute a multi line command
        if [[ $( grep -q ‘Error’ test.txt && echo true || echo false ) == true ]]; then <= The if statement uses a grep query command to check for the word Error in test.txt, if present it evaluates to true, otherwise false
            echo “Creating issue”
            curl –request POST –header “PRIVATE-TOKEN: $private_token” “https://gitlab.agilux.com.au:44388/api/v4/projects/$CI_PROJECT_ID/issues?title=Unit%20test%20failed&description=$(cat test.txt | python3 -c “import urllib.parse, sys; print(urllib.parse.quote(sys.stdin.read()))”)” <= The curl command creates the issue via the API. We set the title Unit Test Failed and the description from the test.txt file URL encoded using the python3 command. The private_token is a personal access token stored in the CI variables. This isn’t best practice, but for demo purposes and since i’m the only user on this gitlab instance, i’ll let that little security issue pass 🙈
            exit 1 <= Tell the pipeline to fail
        else
            echo “success”
            exit 0
        fi
  dependencies:
    – abap_unit <= Tell the job that it needs the artefacts coming from the abap_unit job.

Now when we run our abap_unit job will always execute as success, but the second will validate the output from the abap_unit job and decide whether to create an issue or not.
Checking the new pipeline we can see that the first job passes now as expected, but second fails.

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

And upon checking our issue log, we now have a newly created issue

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Text looks a bit weird as Gitlab expects markdown formatted text, but currently it’s not.
You could take a look at pandoc to handle that.
Also if you want to see the actual test output as well, then add the line
cat test.txt before the exit 1 line.
You could also assign the user to the issue automatically by adding the parameter assignee_id=$GITLAB_USER_ID to the curl command.

SAP ABAP Development, ABAP Testing and Analysis, SAP ABAP Exam Prep, SAP ABAP Certification, SAP ABAP Career, SAP ABAP Guides, SAP ABAP Skills, SAP ABAP Jobs

Source: sap.com

No comments:

Post a Comment