diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 56c0e1b7d49ac9c19ac72b6e1bbc0d7be7b1191c..108611c36c5e60eff5021ae26f822439706b1a27 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -37,13 +37,26 @@ test:
   needs:
     - build
   script:
-    - ./gradlew --console=plain --no-daemon allTests
+    - ./gradlew --console=plain --no-daemon allTests jacocoTestReport
   artifacts:
     when: always
+    paths:
+      - "ragconnect.tests/build/reports/jacoco/all-tests/jacoco*Report.xml"
     reports:
       junit: "ragconnect.tests/build/test-results/**/TEST-*.xml"
     expire_in: 1 week
 
+extractCoverage:
+  image: python:3.8-buster
+  stage: test
+  needs:
+    - test
+  script:
+    - pip install --user untangle
+    - cd ragconnect.tests/
+    - python extract-coverage-from-report.py
+  coverage: "/Covered (\\d{1,3}\\.\\d{2}%) of instructions for all projects\\./"
+
 publish:
   image: openjdk:11
   stage: publish
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index 485a59d61efefe9f77592c1f62ca485db087da98..33ce1df60e6714e2200efb1792132b4a4cdac394 100644
--- a/ragconnect.tests/build.gradle
+++ b/ragconnect.tests/build.gradle
@@ -22,6 +22,7 @@ plugins {
     id 'idea'
     id 'com.github.ben-manes.versions' version '0.36.0'
     id 'com.google.protobuf' version "0.8.14"
+    id 'jacoco'
 }
 
 apply plugin: 'jastadd'
@@ -83,6 +84,17 @@ protobuf {
     }
 }
 
+jacocoTestReport {
+    executionData tasks.withType(Test).findAll { it.state.executed }
+    getExecutionData().setFrom(fileTree(buildDir).include("/jacoco/*.exec"))
+
+    reports {
+        xml.enabled true
+        xml.destination(file("${jacoco.reportsDir}/all-tests/jacocoAllTestReport.xml"))
+        html.enabled false
+    }
+}
+
 task allTests(type: Test, dependsOn: testClasses) {
     description = 'Run every test'
     group = 'verification'
diff --git a/ragconnect.tests/extract-coverage-from-report.py b/ragconnect.tests/extract-coverage-from-report.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8e2668407874abdec7a67c1a8dda1093fb015af
--- /dev/null
+++ b/ragconnect.tests/extract-coverage-from-report.py
@@ -0,0 +1,8 @@
+import os
+import untangle
+print('Current path: ' + os.path.abspath(os.curdir))
+obj = untangle.parse('build/reports/jacoco/test/jacocoTestReport.xml')
+instructions = [o for o in obj.report.counter if o['type'] == 'INSTRUCTION'][0]
+missed, covered = int(instructions['missed']), int(instructions['covered'])
+# print missed / (missed + covered)
+print('Covered %.2f%% of instructions for all projects.' % (missed * 100.0 / (missed + covered)))