diff --git a/Jenkinsfile b/Jenkinsfile
index a7b8e5ea476f2261893ba8bbce5d13e3dbd78245..e8a699057d29e9c2c64e7fcf3dc25e31a25d1f8e 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -21,124 +21,144 @@
 // - Docker plugin
 // - ansicolor plugin
 
-
 node('jami-jamsbuildvm-01') {
     // Get number of CPU available for the build
     def cpuCount = sh returnStdout: true, script: 'nproc || echo -n 4'
     def topDir = pwd() + '/jami-jams'
 
-
     stage('SCM Checkout') {
-	    deleteDir()
-	    sh """
-	        git clone --depth=1 --branch=master https://${RING_GERRIT_URL}/jami-jams
-	    """
+        deleteDir()
+        sh '''git clone --depth=1 --branch=master https://${RING_GERRIT_URL}/jami-jams'''
     }
 
-	stage('Build') {
-	    if (env.GERRIT_REFSPEC != null) {
-	        dir (topDir) {
-	            sh """
-	            git fetch origin ${GERRIT_REFSPEC} && git checkout FETCH_HEAD
-	            docker build -f Dockerfile -t jams:latest --target prod .
-	            mkdir coverage
-	            CONTAINER=\$(docker create jams:latest) \
-	                && docker cp \$CONTAINER:/app/jams/. jams \
-	                && docker cp \$CONTAINER:/app/versions.json . \
-	                && docker cp \$CONTAINER:/app/datastore/target/site/jacoco/. coverage/datastore  \
-	                && docker cp \$CONTAINER:/app/jams-ca/target/site/jacoco/. coverage/jams-ca  \
-	                && docker cp \$CONTAINER:/app/jams-common/target/site/jacoco/. coverage/jams-common  \
-	                && docker cp \$CONTAINER:/app/jams-server/target/site/jacoco/. coverage/jams-server  \
-	                && docker cp \$CONTAINER:/app/ldap-connector/target/site/jacoco/. coverage/ldap-connector  \
-	                && docker rm -v \$CONTAINER
-	            """
-	        }
-	    } else {
-	        error "FAILED: I don't know what to build"
-	    }
+    stage('Test') {
+        if (env.GERRIT_REFSPEC != null) {
+            dir(topDir) {
+                sh '''git fetch origin ${GERRIT_REFSPEC} && git checkout FETCH_HEAD'''
+
+                parallel(
+                    "test-jdk-11": {
+                        sh '''
+                        docker build -f extras/CI/Dockerfile.test -t jams-test:11 --build-arg JAVA_VERSION=11 .
+                        docker run jams-test:11 mvn verify
+                        '''
+                    },
+                    "test-jdk-17": {
+                        sh '''
+                        docker build -f extras/CI/Dockerfile.test -t jams-test:17 --build-arg JAVA_VERSION=17 .
+                        docker run jams-test:17 mvn verify
+                        '''
+                    },
+                    "test-jdk-21": {
+                        sh '''
+                        docker build -f extras/CI/Dockerfile.test -t jams-test:21 --build-arg JAVA_VERSION=21 .
+                        docker run jams-test:21 mvn verify
+                        '''
+                    },
+                    failFast: true // Aborts the parallel steps if one of them fails
+                )
+            }
+        }  else {
+            error "FAILED: I don't know what to test"
+        }
     }
 
-    stage('Deploy') {
-        dir (topDir) {
-            sh """
-            tar -cvf jams.tar jams/*
-            """
+    if (env.BUILD_TYPE != ('default')) {
+        stage('Build') {
+            if (env.GERRIT_REFSPEC != null) {
+                dir(topDir) {
+                    sh """
+                    git fetch origin ${GERRIT_REFSPEC} && git checkout FETCH_HEAD
+                    docker build -f Dockerfile -t jams:latest --target prod .
+                    mkdir coverage
+                    CONTAINER=\$(docker create jams:latest) \
+                        && docker cp \$CONTAINER:/app/jams/. jams \
+                        && docker cp \$CONTAINER:/app/versions.json . \
+                        && docker cp \$CONTAINER:/app/datastore/target/site/jacoco/. coverage/datastore  \
+                        && docker cp \$CONTAINER:/app/jams-ca/target/site/jacoco/. coverage/jams-ca  \
+                        && docker cp \$CONTAINER:/app/jams-common/target/site/jacoco/. coverage/jams-common  \
+                        && docker cp \$CONTAINER:/app/jams-server/target/site/jacoco/. coverage/jams-server  \
+                        && docker cp \$CONTAINER:/app/ldap-connector/target/site/jacoco/. coverage/ldap-connector  \
+                        && docker rm -v \$CONTAINER
+                    """
+                }
+            } else {
+                error "FAILED: I don't know what to build"
+            }
+        }
 
-            def DEPLOY_ENV_NAME = env.BUILD_TYPE.split("::")[0]
-            def DEPLOY_ENV_IP = env.BUILD_TYPE.split("::")[1]
-            echo "Build type: ${env.BUILD_TYPE}"
-            echo "Build name: ${DEPLOY_ENV_NAME}"
+        stage('Deploy') {
+            dir(topDir) {
+                sh '''tar -cvf jams.tar jams/*'''
 
-            if(DEPLOY_ENV_NAME.equals("staging")){
-                sh """
-                    scp -o StrictHostKeyChecking=no -r jams/* root@${DEPLOY_ENV_IP}:/opt/jams-local
-                    ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/crl.pem'
-                    ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/config.json'
-                    ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/keystore.jks'
-                    ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/oauth.*'
-                    ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/tomcat.8080'
-                    ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/jams'
-                    ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/derby.log'
-                    ssh root@${DEPLOY_ENV_IP} 'systemctl restart jams-local'
-                """
-            }
-            if(DEPLOY_ENV_NAME.equals("qa")){
-                sh """
-                    ls -la .
-                    scp -r jams/* root@${DEPLOY_ENV_IP}:/opt/jams
-                    ssh root@${DEPLOY_ENV_IP} 'systemctl restart jams-prod'
-                """
-            }
-            if(DEPLOY_ENV_NAME.equals("qa-local")){
-                sh """
-                    scp -r jams/* root@${DEPLOY_ENV_IP}:/opt/jams-local
-                    ssh root@${DEPLOY_ENV_IP} 'systemctl restart jams-local'
-                """
-            }
-            if(DEPLOY_ENV_NAME.equals("qa-ad")){
-                sh """
-                    scp -r jams/* root@${DEPLOY_ENV_IP}:/opt/jams-local
-                    ssh root@${DEPLOY_ENV_IP} 'systemctl restart jams-local'
-                """
-            }
-            if(DEPLOY_ENV_NAME.equals("publish-update")){
-                sh """
-                    ssh root@${DEPLOY_ENV_IP} 'rm -r /var/www/html/updates/' || true
-                    ssh root@${DEPLOY_ENV_IP} 'rm /var/www/html/versions.json' || true
-                    ssh root@${DEPLOY_ENV_IP} 'mkdir /var/www/html/updates' || true
-                    scp -r jams/* root@${DEPLOY_ENV_IP}:/var/www/html/updates || true
-                    ssh root@${DEPLOY_ENV_IP} 'mv /var/www/html/updates/libs/* /var/www/html/updates/' || true
-                    ssh root@${DEPLOY_ENV_IP} 'rm -r /var/www/html/updates/libs/' || true
-                    scp versions.json root@${DEPLOY_ENV_IP}:/var/www/html/
-                """
-            }
-            if(DEPLOY_ENV_NAME.equals("dl")){
+                def DEPLOY_ENV_NAME = env.BUILD_TYPE.split('::')[0]
+                def DEPLOY_ENV_IP = env.BUILD_TYPE.split('::')[1]
+                echo "Build type: ${env.BUILD_TYPE}"
+                echo "Build name: ${DEPLOY_ENV_NAME}"
 
-                def remoteHost = env.SSH_HOST_DL_RING_CX
-                def remoteBaseDir = '/srv/repository/ring/jams'
-                def JENKINS_SSH_KEY = '35cefd32-dd99-41b0-8312-0b386df306ff'
-                def DL_SSH_KEY = '5825b39b-dfc6-435f-918e-12acc1f56221'
+                if (DEPLOY_ENV_NAME.equals('staging')) {
+                    sh """
+                        scp -o StrictHostKeyChecking=no -r jams/* root@${DEPLOY_ENV_IP}:/opt/jams-local
+                        ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/crl.pem'
+                        ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/config.json'
+                        ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/keystore.jks'
+                        ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/oauth.*'
+                        ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/tomcat.8080'
+                        ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/jams'
+                        ssh root@${DEPLOY_ENV_IP} 'rm -rf /opt/jams-local/derby.log'
+                        ssh root@${DEPLOY_ENV_IP} 'systemctl restart jams-local'
+                    """
+                }
+                if (DEPLOY_ENV_NAME.equals('qa')) {
+                    sh """
+                        ls -la .
+                        scp -r jams/* root@${DEPLOY_ENV_IP}:/opt/jams
+                        ssh root@${DEPLOY_ENV_IP} 'systemctl restart jams-prod'
+                    """
+                }
+                if (DEPLOY_ENV_NAME.equals('qa-local')) {
+                    sh """
+                        scp -r jams/* root@${DEPLOY_ENV_IP}:/opt/jams-local
+                        ssh root@${DEPLOY_ENV_IP} 'systemctl restart jams-local'
+                    """
+                }
+                if (DEPLOY_ENV_NAME.equals('publish-update')) {
+                    sh """
+                        ssh root@${DEPLOY_ENV_IP} 'rm -r /var/www/html/updates/' || true
+                        ssh root@${DEPLOY_ENV_IP} 'rm /var/www/html/versions.json' || true
+                        ssh root@${DEPLOY_ENV_IP} 'mkdir /var/www/html/updates' || true
+                        scp -r jams/* root@${DEPLOY_ENV_IP}:/var/www/html/updates || true
+                        ssh root@${DEPLOY_ENV_IP} 'mv /var/www/html/updates/libs/* /var/www/html/updates/' || true
+                        ssh root@${DEPLOY_ENV_IP} 'rm -r /var/www/html/updates/libs/' || true
+                        scp versions.json root@${DEPLOY_ENV_IP}:/var/www/html/
+                    """
+                }
+                if (DEPLOY_ENV_NAME.equals('dl')) {
+                    def remoteHost = env.SSH_HOST_DL_RING_CX
+                    def remoteBaseDir = '/srv/repository/ring/jams'
+                    def JENKINS_SSH_KEY = '35cefd32-dd99-41b0-8312-0b386df306ff'
+                    def DL_SSH_KEY = '5825b39b-dfc6-435f-918e-12acc1f56221'
 
-                sshagent(credentials: [JENKINS_SSH_KEY, DL_SSH_KEY]) {
-                    echo "Publishing to dl.jami.net..."
-                    script {
+                    sshagent(credentials: [JENKINS_SSH_KEY, DL_SSH_KEY]) {
+                        echo 'Publishing to dl.jami.net...'
+                        script {
                             sh """
                                 tar -cvf jams.tar jams/*
                                 rsync --verbose jams.tar ${remoteHost}:${remoteBaseDir}
                             """
-                    }
+                        }
 
-                    sh("ssh ${remoteHost} mkdir -p ${remoteBaseDir}/docs")
-                    sh("rsync -a coverage/ ${remoteHost}:${remoteBaseDir}/docs/coverage/")
+                        sh("ssh ${remoteHost} mkdir -p ${remoteBaseDir}/docs")
+                        sh("rsync -a coverage/ ${remoteHost}:${remoteBaseDir}/docs/coverage/")
+                    }
                 }
             }
         }
-    }
 
-    stage('Post'){
-        dir (topDir) {
-            archiveArtifacts artifacts: 'jams.tar', fingerprint: true
+        stage('Post') {
+            dir(topDir) {
+                archiveArtifacts artifacts: 'jams.tar', fingerprint: true
+            }
         }
     }
 }
-
diff --git a/extras/CI/Dockerfile.test b/extras/CI/Dockerfile.test
new file mode 100644
index 0000000000000000000000000000000000000000..b25fc23a97a01b353ddcb94044245bda2412589d
--- /dev/null
+++ b/extras/CI/Dockerfile.test
@@ -0,0 +1,16 @@
+ARG JAVA_VERSION=11
+
+FROM maven:3.9.6-eclipse-temurin-${JAVA_VERSION}
+
+WORKDIR /test
+COPY pom.xml .
+COPY ad-connector/pom.xml ad-connector/pom.xml
+COPY authentication-module/pom.xml authentication-module/pom.xml
+COPY datastore/pom.xml datastore/pom.xml
+COPY jami-dht/pom.xml jami-dht/pom.xml
+COPY jami-nameserver/pom.xml jami-nameserver/pom.xml
+COPY jams-ca/pom.xml jams-ca/pom.xml
+COPY jams-common/pom.xml jams-common/pom.xml
+COPY jams-launcher/pom.xml jams-launcher/pom.xml
+COPY ldap-connector/pom.xml ldap-connector/pom.xml
+COPY jams-server/pom.xml jams-server/pom.xml