You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2020/01/30 16:46:15 UTC

[syncope] branch master updated: [provisioning-api] Reach at least 80% of Sonarqube coverage (#160)

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new cb88d8a  [provisioning-api] Reach at least 80% of Sonarqube coverage (#160)
cb88d8a is described below

commit cb88d8a55dcd28656886e11e3159624047aa71b1
Author: Davide Cortellucci <da...@gmail.com>
AuthorDate: Thu Jan 30 16:56:52 2020 +0100

    [provisioning-api] Reach at least 80% of Sonarqube coverage (#160)
---
 core/provisioning-api/pom.xml                      |   4 +
 .../core/provisioning/api/AccountGetter.java       |   1 +
 .../api/AnyObjectProvisioningManager.java          |   3 +-
 .../core/provisioning/api/AuditEntryImpl.java      |   1 +
 .../core/provisioning/api/AuditManager.java        |   1 +
 .../provisioning/api/GroupProvisioningManager.java |   3 +-
 .../core/provisioning/api/IntAttrNameParser.java   |  13 +-
 .../core/provisioning/api/PlainAttrGetter.java     |   1 +
 .../provisioning/api/PropagationByResource.java    |   8 +-
 .../core/provisioning/api/ProvisioningManager.java |   3 +-
 .../provisioning/api/UserProvisioningManager.java  |   3 +-
 .../provisioning/api/cache/VirAttrCacheKey.java    |   1 +
 .../provisioning/api/event/AfterHandlingEvent.java |   1 +
 .../core/provisioning/api/jexl/JexlUtils.java      |  44 +++--
 .../api/jexl/SyncopeJexlFunctions.java             |   7 +-
 .../core/provisioning/api/job/JobManager.java      |   1 +
 .../api/notification/NotificationManager.java      |   1 +
 .../api/propagation/PropagationManager.java        |   1 +
 .../api/propagation/PropagationTaskExecutor.java   |   1 +
 .../api/propagation/PropagationTaskInfo.java       |   1 +
 .../api/pushpull/ReconFilterBuilder.java           |   4 +-
 .../api/serialization/AttributeDeserializer.java   |  15 +-
 .../serialization/GuardedStringDeserializer.java   |  51 +++---
 .../api/serialization/GuardedStringSerializer.java |  37 +++--
 .../api/serialization/SyncTokenDeserializer.java   |  22 +--
 .../core/provisioning/api/utils/FormatUtils.java   |  17 +-
 .../core/provisioning/api/AbstractTest.java}       |  20 ++-
 .../core/provisioning/api/AuditEntryImplTest.java  |  93 +++++++++++
 .../provisioning/api/IntAttrNameParserTest.java    |  46 ++---
 .../api/PropagationByResourceTest.java             | 185 +++++++++++++++++++++
 .../provisioning/api/UserWorkflowResultTest.java   |  58 +++++++
 .../core/provisioning/api/WorkflowResultTest.java  |  52 ++++++
 .../api/cache/VirAttrCacheKeyTest.java             |  53 ++++++
 .../api/cache/VirAttrCacheValueTest.java           |  50 ++++++
 .../api/event/AfterHandlingEventTest.java          |  63 +++++++
 .../core/provisioning/api/job/JobNamerTest.java    |  81 +++++++++
 .../api/propagation/PropagationTaskInfoTest.java   |  71 ++++++++
 .../api/pushpull/ProvisioningProfileTest.java      |  55 ++++++
 .../serialization/AttributeDeserializerTest.java   | 131 +++++++++++++++
 .../api/serialization/AttributeSerializerTest.java |  81 +++++++++
 .../GuardedStringDeserializerTest.java             |  89 ++++++++++
 .../serialization/GuardedStringSerializerTest.java |  56 +++++++
 .../api/serialization/POJOHelperTest.java          |  58 +++++++
 .../serialization/SyncTokenDeserializerTest.java   | 104 ++++++++++++
 .../api/serialization/SyncTokenSerializerTest.java |  72 ++++++++
 .../api/utils/ConnPoolConfUtilsTest.java           |  57 +++++++
 .../provisioning/api/utils/FormatUtilsTest.java    |  97 +++++++++++
 .../core/provisioning/api/utils/JexlUtilsTest.java | 168 +++++++++++++++++++
 .../provisioning/api/utils/RealmUtilsTest.java     |  67 ++++++++
 .../core/provisioning/api/utils/URIUtilsTest.java  |  45 +++++
 .../org.mockito.plugins.MockMaker                  |  18 ++
 .../java/pushpull/AbstractPullResultHandler.java   |   2 +-
 .../DefaultAnyObjectPullResultHandler.java         |   2 +-
 .../pushpull/DefaultGroupPullResultHandler.java    |   2 +-
 .../pushpull/DefaultUserPullResultHandler.java     |   2 +-
 .../java/pushpull/DBPasswordPullActionsTest.java   |   8 +
 56 files changed, 2000 insertions(+), 131 deletions(-)

diff --git a/core/provisioning-api/pom.xml b/core/provisioning-api/pom.xml
index f803af7..6da0358 100644
--- a/core/provisioning-api/pom.xml
+++ b/core/provisioning-api/pom.xml
@@ -62,6 +62,10 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
+      <artifactId>spring-test</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
       <artifactId>spring-tx</artifactId>
     </dependency>
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AccountGetter.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AccountGetter.java
index c4ff098..815b577 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AccountGetter.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AccountGetter.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.provisioning.api;
 import java.util.function.Function;
 import org.apache.syncope.core.persistence.api.entity.user.Account;
 
+@SuppressWarnings("squid:S1214")
 public interface AccountGetter extends Function<Account, Account> {
 
     AccountGetter DEFAULT = account -> account;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
index 7fe4064..0036c85 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AnyObjectProvisioningManager.java
@@ -24,10 +24,9 @@ import java.util.Set;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.request.AnyObjectCR;
 import org.apache.syncope.common.lib.request.AnyObjectUR;
-import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 
-public interface AnyObjectProvisioningManager extends ProvisioningManager<AnyObjectTO, AnyObjectCR, AnyObjectUR> {
+public interface AnyObjectProvisioningManager extends ProvisioningManager<AnyObjectCR, AnyObjectUR> {
 
     Pair<String, List<PropagationStatus>> create(
             AnyObjectCR anyObjectCR, Set<String> excludedResources, boolean nullPriorityAsync);
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java
index 9681919..3d6b949 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java
@@ -29,6 +29,7 @@ import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AuditLoggerName;
 import org.apache.syncope.core.persistence.api.entity.AuditEntry;
 
+@SuppressWarnings({ "squid:S1450", "squid:S1948" })
 public class AuditEntryImpl implements AuditEntry {
 
     private static final long serialVersionUID = -2299082316063743582L;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditManager.java
index a59d77d..70835c2 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditManager.java
@@ -60,6 +60,7 @@ public interface AuditManager {
      * @param output object(s) produced by the event
      * @param input object(s) provided to the event
      */
+    @SuppressWarnings("squid:S00107")
     void audit(
             String who,
             AuditElements.EventCategoryType type,
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/GroupProvisioningManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/GroupProvisioningManager.java
index 4278be4..9569105 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/GroupProvisioningManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/GroupProvisioningManager.java
@@ -26,9 +26,8 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.request.GroupCR;
 import org.apache.syncope.common.lib.request.GroupUR;
 import org.apache.syncope.common.lib.to.PropagationStatus;
-import org.apache.syncope.common.lib.to.GroupTO;
 
-public interface GroupProvisioningManager extends ProvisioningManager<GroupTO, GroupCR, GroupUR> {
+public interface GroupProvisioningManager extends ProvisioningManager<GroupCR, GroupUR> {
 
     Pair<String, List<PropagationStatus>> create(
             GroupCR groupCR,
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java
index fad50f4..cf5bff5 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java
@@ -33,26 +33,29 @@ import org.apache.syncope.core.persistence.api.entity.Schema;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
+@SuppressWarnings({ "squid:S4784", "squid:S3776" })
 public class IntAttrNameParser {
+    
+    private static final String END_PATTERN = ")\\]\\.(.+)";
 
     private static final Pattern PRIVILEGE_PATTERN = Pattern.compile(
             "^privileges\\[(" + SyncopeConstants.NAME_PATTERN + ")\\]");
 
     private static final Pattern ENCLOSING_GROUP_PATTERN = Pattern.compile(
-            "^groups\\[(" + SyncopeConstants.NAME_PATTERN + ")\\]\\.(.+)");
+            "^groups\\[(" + SyncopeConstants.NAME_PATTERN + END_PATTERN);
 
     private static final Pattern RELATED_USER_PATTERN = Pattern.compile(
-            "^users\\[(" + SyncopeConstants.NAME_PATTERN + ")\\]\\.(.+)");
+            "^users\\[(" + SyncopeConstants.NAME_PATTERN + END_PATTERN);
 
     private static final Pattern RELATED_ANY_OBJECT_PATTERN = Pattern.compile(
-            "^anyObjects\\[(" + SyncopeConstants.NAME_PATTERN + ")\\]\\.(.+)");
+            "^anyObjects\\[(" + SyncopeConstants.NAME_PATTERN + END_PATTERN);
 
     private static final Pattern MEMBERSHIP_PATTERN = Pattern.compile(
-            "^memberships\\[(" + SyncopeConstants.NAME_PATTERN + ")\\]\\.(.+)");
+            "^memberships\\[(" + SyncopeConstants.NAME_PATTERN + END_PATTERN);
 
     private static final Pattern RELATIONSHIP_PATTERN = Pattern.compile(
             "^relationships\\[(" + SyncopeConstants.NAME_PATTERN + ")\\]"
-            + "\\[(" + SyncopeConstants.NAME_PATTERN + ")\\]\\.(.+)");
+            + "\\[(" + SyncopeConstants.NAME_PATTERN + END_PATTERN);
 
     @Autowired
     private PlainSchemaDAO plainSchemaDAO;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java
index 763ad10..de0d306 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java
@@ -22,6 +22,7 @@ import java.util.function.BiFunction;
 import org.apache.syncope.core.persistence.api.entity.Attributable;
 import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 
+@SuppressWarnings("squid:S1214")
 public interface PlainAttrGetter extends BiFunction<Attributable<? extends PlainAttr<?>>, String, PlainAttr<?>> {
 
     PlainAttrGetter DEFAULT = (attributable, schema) -> attributable.getPlainAttr(schema).orElse(null);
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PropagationByResource.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PropagationByResource.java
index 73626b7..9c71d67 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PropagationByResource.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PropagationByResource.java
@@ -198,8 +198,8 @@ public class PropagationByResource<T extends Serializable> implements Serializab
      */
     public boolean removeAll(final Collection<T> keys) {
         return toBeCreated.removeAll(keys)
-                | toBeUpdated.removeAll(keys)
-                | toBeDeleted.removeAll(keys);
+                || toBeUpdated.removeAll(keys)
+                || toBeDeleted.removeAll(keys);
     }
 
     /**
@@ -212,8 +212,8 @@ public class PropagationByResource<T extends Serializable> implements Serializab
      */
     public boolean retainAll(final Collection<T> keys) {
         return toBeCreated.retainAll(keys)
-                | toBeUpdated.retainAll(keys)
-                | toBeDeleted.retainAll(keys);
+                || toBeUpdated.retainAll(keys)
+                || toBeDeleted.retainAll(keys);
     }
 
     public boolean contains(final ResourceOperation type, final T key) {
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
index 99ab5b1..b4e8935 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisioningManager.java
@@ -24,10 +24,9 @@ import java.util.Set;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.request.AnyCR;
 import org.apache.syncope.common.lib.request.AnyUR;
-import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 
-public interface ProvisioningManager<T extends AnyTO, C extends AnyCR, U extends AnyUR> {
+public interface ProvisioningManager<C extends AnyCR, U extends AnyUR> {
 
     Pair<String, List<PropagationStatus>> create(C anyCR, boolean nullPriorityAsync);
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java
index 4ddccca..72e4fc9 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java
@@ -27,9 +27,8 @@ import org.apache.syncope.common.lib.request.UserCR;
 import org.apache.syncope.common.lib.request.UserUR;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.ProvisioningReport;
-import org.apache.syncope.common.lib.to.UserTO;
 
-public interface UserProvisioningManager extends ProvisioningManager<UserTO, UserCR, UserUR> {
+public interface UserProvisioningManager extends ProvisioningManager<UserCR, UserUR> {
 
     Pair<String, List<PropagationStatus>> activate(StatusR statusR, boolean nullPriorityAsync);
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheKey.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheKey.java
index 59e1839..dd394f3 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheKey.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheKey.java
@@ -23,6 +23,7 @@ import java.util.Objects;
 /**
  * Cache entry key.
  */
+@SuppressWarnings("squid:S2065")
 public class VirAttrCacheKey {
 
     /**
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEvent.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEvent.java
index 4e14ee2..4be816b 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEvent.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEvent.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.provisioning.api.event;
 import java.io.Serializable;
 import org.apache.syncope.common.lib.types.AuditElements;
 
+@SuppressWarnings({ "squid:S00107", "squid:S1948" })
 public class AfterHandlingEvent implements Serializable {
 
     private static final long serialVersionUID = 5950986229089263378L;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
index ba0e63f..05c9c0f 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
@@ -52,10 +52,12 @@ import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.provisioning.api.DerAttrHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.util.ReflectionUtils;
 
 /**
  * JEXL <a href="http://commons.apache.org/jexl/reference/index.html">reference</a> is available.
  */
+@SuppressWarnings({ "squid:S3008", "squid:S3776", "squid:S1141" })
 public final class JexlUtils {
 
     private static final Logger LOG = LoggerFactory.getLogger(JexlUtils.class);
@@ -74,7 +76,7 @@ public final class JexlUtils {
                 JEXL_ENGINE = new JexlBuilder().
                         uberspect(new ClassFreeUberspect()).
                         loader(new EmptyClassLoader()).
-                        namespaces(Collections.<String, Object>singletonMap("syncope", new SyncopeJexlFunctions())).
+                        namespaces(Map.of("syncope", new SyncopeJexlFunctions())).
                         cache(512).
                         silent(false).
                         strict(false).
@@ -164,17 +166,19 @@ public final class JexlUtils {
                 Object fieldValue = null;
                 if (fd.getLeft().getReadMethod() == null) {
                     if (fd.getRight() != null) {
-                        fd.getRight().setAccessible(true);
+                        ReflectionUtils.makeAccessible(fd.getRight());
                         fieldValue = fd.getRight().get(object);
                     }
                 } else {
                     fieldValue = fd.getLeft().getReadMethod().invoke(object);
                 }
-                fieldValue = fieldValue == null
-                        ? StringUtils.EMPTY
-                        : (fieldType.equals(Date.class)
-                        ? FormatUtils.format((Date) fieldValue, false)
-                        : fieldValue);
+                if (fieldValue == null) {
+                    fieldValue = StringUtils.EMPTY;
+                } else {
+                    fieldValue = fieldType.equals(Date.class)
+                            ? FormatUtils.format((Date) fieldValue, false)
+                            : fieldValue;
+                }
 
                 jexlContext.set(fieldName, fieldValue);
 
@@ -197,11 +201,14 @@ public final class JexlUtils {
 
     public static void addAttrsToContext(final Collection<Attr> attrs, final JexlContext jexlContext) {
         attrs.stream().filter(attr -> attr.getSchema() != null).forEach(attr -> {
-            Object value = attr.getValues().isEmpty()
-                    ? StringUtils.EMPTY
-                    : attr.getValues().size() == 1
-                    ? attr.getValues().get(0)
-                    : attr.getValues();
+            Object value;
+            if (attr.getValues().isEmpty()) {
+                value = StringUtils.EMPTY;
+            } else {
+                value = attr.getValues().size() == 1
+                        ? attr.getValues().get(0)
+                        : attr.getValues();
+            }
 
             LOG.debug("Add attribute {} with value {}", attr.getSchema(), value);
 
@@ -214,11 +221,14 @@ public final class JexlUtils {
 
         attrs.stream().filter(attr -> attr.getSchema() != null).forEach(attr -> {
             List<String> attrValues = attr.getValuesAsStrings();
-            Object value = attrValues.isEmpty()
-                    ? StringUtils.EMPTY
-                    : attrValues.size() == 1
-                    ? attrValues.get(0)
-                    : attrValues;
+            Object value;
+            if (attrValues.isEmpty()) {
+                value = StringUtils.EMPTY;
+            } else {
+                value = attrValues.size() == 1
+                        ? attrValues.get(0)
+                        : attrValues;
+            }
 
             LOG.debug("Add attribute {} with value {}", attr.getSchema().getKey(), value);
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
index 26d695a..b2a566a 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
@@ -28,9 +28,6 @@ import org.apache.commons.lang3.StringUtils;
  */
 public class SyncopeJexlFunctions {
 
-    protected SyncopeJexlFunctions() {
-    }
-
     /**
      * Converts realm's full path into the equivalent DN.
      *
@@ -40,7 +37,7 @@ public class SyncopeJexlFunctions {
      * @param attr attribute name for DN
      * @return DN equivalent of the provided full path
      */
-    public static String fullPath2Dn(final String fullPath, final String attr) {
+    public String fullPath2Dn(final String fullPath, final String attr) {
         return fullPath2Dn(fullPath, attr, StringUtils.EMPTY);
     }
 
@@ -55,7 +52,7 @@ public class SyncopeJexlFunctions {
      * @param prefix result's prefix
      * @return DN equivalent of the provided full path
      */
-    public static String fullPath2Dn(final String fullPath, final String attr, final String prefix) {
+    public String fullPath2Dn(final String fullPath, final String attr, final String prefix) {
         String[] fullPathSplitted = fullPath.split("/");
         if (fullPathSplitted.length <= 1) {
             return StringUtils.EMPTY;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobManager.java
index ed34fc3..6d8fa28 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobManager.java
@@ -27,6 +27,7 @@ import org.quartz.JobKey;
 import org.quartz.Scheduler;
 import org.quartz.SchedulerException;
 
+@SuppressWarnings("squid:S1214")
 public interface JobManager {
 
     String DOMAIN_KEY = "domain";
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java
index 0bdbf01..f130536 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/notification/NotificationManager.java
@@ -76,6 +76,7 @@ public interface NotificationManager {
      * @param input object(s) provided to the event
      * @return created notification tasks
      */
+    @SuppressWarnings("squid:S00107")
     List<NotificationTask> createTasks(
             String who,
             AuditElements.EventCategoryType type,
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
index 048f06b..dec9af3 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
@@ -37,6 +37,7 @@ import org.apache.syncope.core.provisioning.api.DerAttrHandler;
 import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
 import org.identityconnectors.framework.common.objects.Attribute;
 
+@SuppressWarnings("squid:S00107")
 public interface PropagationManager {
 
     /**
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
index 50455b6..3140378 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
@@ -27,6 +27,7 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
  *
  * @see PropagationTaskTO
  */
+@SuppressWarnings("squid:S1214")
 public interface PropagationTaskExecutor {
 
     /**
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
index c3b3e44..ca5a33f 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
@@ -26,6 +26,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.provisioning.api.Connector;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 
+@SuppressWarnings("squid:S1948")
 public class PropagationTaskInfo extends PropagationTaskTO {
 
     private static final long serialVersionUID = -2879861567335503099L;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ReconFilterBuilder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ReconFilterBuilder.java
index e5d3d8b..50bc90f 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ReconFilterBuilder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ReconFilterBuilder.java
@@ -28,10 +28,8 @@ import org.identityconnectors.framework.impl.api.local.operations.FilteredResult
  */
 public interface ReconFilterBuilder {
 
-    FilteredResultsHandler.PassThroughFilter PASS_THROUGH = new FilteredResultsHandler.PassThroughFilter();
-
     default Filter build() {
-        return PASS_THROUGH;
+        return new FilteredResultsHandler.PassThroughFilter();
     }
 
     default OperationOptions build(final OperationOptions initialOptions) {
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializer.java
index 2144080..2f050b7 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializer.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializer.java
@@ -34,6 +34,7 @@ import org.identityconnectors.framework.common.objects.AttributeBuilder;
 import org.identityconnectors.framework.common.objects.Name;
 import org.identityconnectors.framework.common.objects.Uid;
 
+@SuppressWarnings("squid:S3776")
 class AttributeDeserializer extends JsonDeserializer<Attribute> {
 
     @Override
@@ -71,11 +72,15 @@ class AttributeDeserializer extends JsonDeserializer<Attribute> {
             }
         }
 
-        return Uid.NAME.equals(name)
-                ? new Uid(values.isEmpty() || values.get(0) == null ? null : values.get(0).toString())
-                : Name.NAME.equals(name)
-                ? new Name(values.isEmpty() || values.get(0) == null ? null : values.get(0).toString())
-                : AttributeBuilder.build(name, values);
+        if (Uid.NAME.equals(name)) {
+            return new Uid(values.isEmpty() || values.get(0) == null ? null : values.get(0).toString());
+        } else {
+            if (Name.NAME.equals(name)) {
+                return new Name(values.isEmpty() || values.get(0) == null ? null : values.get(0).toString());
+            } else {
+                return AttributeBuilder.build(name, values);
+            }
+        }
     }
 
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializer.java
index af4378a..d69a2f4 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializer.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializer.java
@@ -29,11 +29,22 @@ import org.identityconnectors.common.security.EncryptorFactory;
 import org.identityconnectors.common.security.GuardedString;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.util.ReflectionUtils;
 
 class GuardedStringDeserializer extends JsonDeserializer<GuardedString> {
 
     private static final Logger LOG = LoggerFactory.getLogger(GuardedStringDeserializer.class);
 
+    private static final String READONLY = "readOnly";
+
+    private static final String DISPOSED = "disposed";
+
+    private static final String ENCRYPTED_BYTES = "encryptedBytes";
+
+    private static final String BASE64_SHA1_HASH = "base64SHA1Hash";
+
+    private static final String LOG_ERROR_MESSAGE = "Could not set field value to {}";
+    
     @Override
     public GuardedString deserialize(final JsonParser jp, final DeserializationContext ctx)
             throws IOException {
@@ -41,20 +52,20 @@ class GuardedStringDeserializer extends JsonDeserializer<GuardedString> {
         ObjectNode tree = jp.readValueAsTree();
 
         boolean readOnly = false;
-        if (tree.has("readOnly")) {
-            readOnly = tree.get("readOnly").asBoolean();
+        if (tree.has(READONLY)) {
+            readOnly = tree.get(READONLY).asBoolean();
         }
         boolean disposed = false;
-        if (tree.has("disposed")) {
-            disposed = tree.get("disposed").asBoolean();
+        if (tree.has(DISPOSED)) {
+            disposed = tree.get(DISPOSED).asBoolean();
         }
         byte[] encryptedBytes = null;
-        if (tree.has("encryptedBytes")) {
-            encryptedBytes = Base64.getDecoder().decode(tree.get("encryptedBytes").asText());
+        if (tree.has(ENCRYPTED_BYTES)) {
+            encryptedBytes = Base64.getDecoder().decode(tree.get(ENCRYPTED_BYTES).asText());
         }
         String base64SHA1Hash = null;
-        if (tree.has("base64SHA1Hash")) {
-            base64SHA1Hash = tree.get("base64SHA1Hash").asText();
+        if (tree.has(BASE64_SHA1_HASH)) {
+            base64SHA1Hash = tree.get(BASE64_SHA1_HASH).asText();
         }
 
         final byte[] clearBytes = EncryptorFactory.getInstance().getDefaultEncryptor().decrypt(encryptedBytes);
@@ -62,28 +73,28 @@ class GuardedStringDeserializer extends JsonDeserializer<GuardedString> {
         GuardedString dest = new GuardedString(new String(clearBytes).toCharArray());
 
         try {
-            Field field = GuardedString.class.getDeclaredField("readOnly");
-            field.setAccessible(true);
-            field.setBoolean(dest, readOnly);
+            Field field = GuardedString.class.getDeclaredField(READONLY);
+            ReflectionUtils.makeAccessible(field);
+            ReflectionUtils.setField(field, dest, readOnly);
         } catch (Exception e) {
-            LOG.error("Could not set field value to {}", readOnly, e);
+            LOG.error(LOG_ERROR_MESSAGE, readOnly, e);
         }
 
         try {
-            Field field = GuardedString.class.getDeclaredField("disposed");
-            field.setAccessible(true);
-            field.setBoolean(dest, disposed);
+            Field field = GuardedString.class.getDeclaredField(DISPOSED);
+            ReflectionUtils.makeAccessible(field);
+            ReflectionUtils.setField(field, dest, disposed);
         } catch (Exception e) {
-            LOG.error("Could not set field value to {}", disposed, e);
+            LOG.error(LOG_ERROR_MESSAGE, disposed, e);
         }
 
         if (base64SHA1Hash != null) {
             try {
-                Field field = GuardedString.class.getDeclaredField("base64SHA1Hash");
-                field.setAccessible(true);
-                field.set(dest, base64SHA1Hash);
+                Field field = GuardedString.class.getDeclaredField(BASE64_SHA1_HASH);
+                ReflectionUtils.makeAccessible(field);
+                ReflectionUtils.setField(field, dest, base64SHA1Hash);
             } catch (Exception e) {
-                LOG.error("Could not set field value to {}", base64SHA1Hash, e);
+                LOG.error(LOG_ERROR_MESSAGE, base64SHA1Hash, e);
             }
         }
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializer.java
index 26eec0a..df57608 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializer.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializer.java
@@ -29,11 +29,22 @@ import org.identityconnectors.common.security.GuardedString;
 import org.identityconnectors.common.security.SecurityUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.util.ReflectionUtils;
 
 class GuardedStringSerializer extends JsonSerializer<GuardedString> {
 
     private static final Logger LOG = LoggerFactory.getLogger(GuardedStringSerializer.class);
 
+    private static final String READONLY = "readOnly";
+
+    private static final String DISPOSED = "disposed";
+
+    private static final String ENCRYPTED_BYTES = "encryptedBytes";
+
+    private static final String BASE64_SHA1_HASH = "base64SHA1Hash";
+
+    private static final String LOG_ERROR_MESSAGE = "Could not get field value";
+
     @Override
     public void serialize(final GuardedString source, final JsonGenerator jgen, final SerializerProvider sp)
             throws IOException {
@@ -42,38 +53,38 @@ class GuardedStringSerializer extends JsonSerializer<GuardedString> {
 
         boolean readOnly = false;
         try {
-            Field field = GuardedString.class.getDeclaredField("readOnly");
-            field.setAccessible(true);
+            Field field = GuardedString.class.getDeclaredField(READONLY);
+            ReflectionUtils.makeAccessible(field);
             readOnly = field.getBoolean(source);
         } catch (Exception e) {
-            LOG.error("Could not get field value", e);
+            LOG.error(LOG_ERROR_MESSAGE, e);
         }
-        jgen.writeBooleanField("readOnly", readOnly);
+        jgen.writeBooleanField(READONLY, readOnly);
 
         boolean disposed = false;
         try {
-            Field field = GuardedString.class.getDeclaredField("disposed");
-            field.setAccessible(true);
+            Field field = GuardedString.class.getDeclaredField(DISPOSED);
+            ReflectionUtils.makeAccessible(field);
             disposed = field.getBoolean(source);
         } catch (Exception e) {
-            LOG.error("Could not get field value", e);
+            LOG.error(LOG_ERROR_MESSAGE, e);
         }
-        jgen.writeBooleanField("disposed", disposed);
+        jgen.writeBooleanField(DISPOSED, disposed);
 
         byte[] encryptedBytes =
                 EncryptorFactory.getInstance().getDefaultEncryptor().encrypt(SecurityUtil.decrypt(source).getBytes());
-        jgen.writeStringField("encryptedBytes", Base64.getEncoder().encodeToString(encryptedBytes));
+        jgen.writeStringField(ENCRYPTED_BYTES, Base64.getEncoder().encodeToString(encryptedBytes));
 
         String base64SHA1Hash = null;
         try {
-            Field field = GuardedString.class.getDeclaredField("base64SHA1Hash");
-            field.setAccessible(true);
+            Field field = GuardedString.class.getDeclaredField(BASE64_SHA1_HASH);
+            ReflectionUtils.makeAccessible(field);
             base64SHA1Hash = field.get(source).toString();
         } catch (Exception e) {
-            LOG.error("Could not get field value", e);
+            LOG.error(LOG_ERROR_MESSAGE, e);
         }
         if (base64SHA1Hash != null) {
-            jgen.writeStringField("base64SHA1Hash", base64SHA1Hash);
+            jgen.writeStringField(BASE64_SHA1_HASH, base64SHA1Hash);
         }
 
         jgen.writeEndObject();
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializer.java
index 93522b0..fdf7fb0 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializer.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializer.java
@@ -40,17 +40,17 @@ class SyncTokenDeserializer extends JsonDeserializer<SyncToken> {
         Object value = null;
         if (tree.has("value")) {
             JsonNode node = tree.get("value");
-            value = node.isNull()
-                    ? null
-                    : node.isBoolean()
-                    ? node.asBoolean()
-                    : node.isDouble()
-                    ? node.asDouble()
-                    : node.isLong()
-                    ? node.asLong()
-                    : node.isInt()
-                    ? node.asInt()
-                    : node.asText();
+            if (node.isBoolean()) {
+                value = node.asBoolean();
+            } else if (node.isDouble()) {
+                value = node.asDouble();
+            } else if (node.isLong()) {
+                value = node.asLong();
+            } else if (node.isInt()) {
+                value = node.asInt();
+            } else {
+                value = node.asText();
+            }
 
             if (value instanceof String) {
                 String base64 = (String) value;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/FormatUtils.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/FormatUtils.java
index 603553f..5210084 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/FormatUtils.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/FormatUtils.java
@@ -32,18 +32,13 @@ import org.apache.syncope.common.lib.SyncopeConstants;
  */
 public final class FormatUtils {
 
-    private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT =
-        ThreadLocal.withInitial(() -> new SimpleDateFormat());
+    private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = ThreadLocal.withInitial(SimpleDateFormat::new);
 
-    private static final ThreadLocal<DecimalFormat> DECIMAL_FORMAT = new ThreadLocal<DecimalFormat>() {
-
-        @Override
-        protected DecimalFormat initialValue() {
-            DecimalFormat df = new DecimalFormat();
-            df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH));
-            return df;
-        }
-    };
+    private static final ThreadLocal<DecimalFormat> DECIMAL_FORMAT = ThreadLocal.withInitial(() -> {
+        DecimalFormat df = new DecimalFormat();
+        df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH));
+        return df;
+    });
 
     public static String format(final Date date) {
         return format(date, true);
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java
similarity index 63%
copy from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java
copy to core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java
index 763ad10..9993be0 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/PlainAttrGetter.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java
@@ -18,12 +18,20 @@
  */
 package org.apache.syncope.core.provisioning.api;
 
-import java.util.function.BiFunction;
-import org.apache.syncope.core.persistence.api.entity.Attributable;
-import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
 
-public interface PlainAttrGetter extends BiFunction<Attributable<? extends PlainAttr<?>>, String, PlainAttr<?>> {
-
-    PlainAttrGetter DEFAULT = (attributable, schema) -> attributable.getPlainAttr(schema).orElse(null);
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.WARN)
+@SuppressWarnings("squid:S2187")
+public class AbstractTest {
 
+    @BeforeEach
+    public void init() {
+        MockitoAnnotations.initMocks(this);
+    }
 }
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AuditEntryImplTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AuditEntryImplTest.java
new file mode 100644
index 0000000..42aff67
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AuditEntryImplTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Date;
+import java.util.UUID;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.syncope.common.lib.request.UserUR;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.springframework.test.util.ReflectionTestUtils;
+
+public class AuditEntryImplTest extends AbstractTest {
+
+    @Mock
+    private AuditLoggerName logger;
+
+    private final String who = "testUser";
+
+    private final Object before = "before";
+
+    private final String output = "output";
+
+    private final String[] input = { "test1", "test2" };
+
+    private final String throwable = "throwable";
+
+    private final String key = UUID.randomUUID().toString();
+
+    private final Date date = new Date();
+
+    @Test
+    public void AuditEntryImpl() {
+        AuditEntryImpl auditEntryImpl = new AuditEntryImpl(who, logger, before, output, input);
+        AuditEntryImpl auditEntryImpl2 = AuditEntryImpl.builder().
+                who(who).
+                before(before).
+                logger(logger).
+                output(output).
+                input(null).
+                date(date).
+                key(key).
+                throwable(throwable).
+                build();
+
+        assertEquals(auditEntryImpl2.getWho(), auditEntryImpl.getWho());
+        assertEquals(auditEntryImpl2.getLogger(), auditEntryImpl.getLogger());
+        assertNotEquals(auditEntryImpl2.getInput(), auditEntryImpl.getInput().length);
+        assertEquals(auditEntryImpl2.getDate(), auditEntryImpl2.getDate());
+        assertEquals(auditEntryImpl2.getThrowable(), auditEntryImpl2.getThrowable());
+    }
+
+    @Test
+    public void AuditEntryImplWithUserTO(@Mock UserTO userTO) {
+        AuditEntryImpl auditEntryImpl = new AuditEntryImpl(who, logger, before, userTO, input);
+        assertTrue(EqualsBuilder.reflectionEquals(SerializationUtils.clone(userTO), auditEntryImpl.getOutput()));
+
+        ReflectionTestUtils.setField(userTO, "password", "testP4ssw0rd!");
+        ReflectionTestUtils.setField(userTO, "securityAnswer", "42");
+        AuditEntryImpl auditEntryImpl2 = new AuditEntryImpl(who, logger, before, userTO, input);
+        assertFalse(EqualsBuilder.reflectionEquals(SerializationUtils.clone(userTO), auditEntryImpl2.getOutput()));
+    }
+
+    @Test
+    public void AuditEntryImplWithUserPatch(@Mock UserUR userUR) {
+        AuditEntryImpl auditEntryImpl = new AuditEntryImpl(who, logger, userUR, output, input);
+        assertTrue(EqualsBuilder.reflectionEquals(SerializationUtils.clone(userUR), auditEntryImpl.getBefore()));
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java
index 1e14e94..f9806b0 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java
@@ -19,12 +19,14 @@
 package org.apache.syncope.core.provisioning.api;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.lenient;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -32,6 +34,7 @@ import java.text.ParseException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.lib.types.SchemaType;
@@ -45,18 +48,11 @@ import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.mockito.junit.jupiter.MockitoSettings;
-import org.mockito.quality.Strictness;
 import org.springframework.util.ReflectionUtils;
 
-@ExtendWith(MockitoExtension.class)
-@MockitoSettings(strictness = Strictness.WARN)
-public class IntAttrNameParserTest {
+public class IntAttrNameParserTest extends AbstractTest {
 
     private static final Map<AnyTypeKind, List<String>> FIELDS = new HashMap<>();
 
@@ -86,51 +82,49 @@ public class IntAttrNameParserTest {
 
     @BeforeEach
     public void initMocks() throws NoSuchFieldException {
-        MockitoAnnotations.initMocks(this);
-
-        when(anyUtilsFactory.getInstance(any(AnyTypeKind.class))).thenAnswer(ic -> {
+        lenient().when(anyUtilsFactory.getInstance(any(AnyTypeKind.class))).thenAnswer(ic -> {
             when(anyUtils.anyTypeKind()).thenReturn(ic.getArgument(0));
             return anyUtils;
         });
-        when(anyUtils.getField(anyString())).thenAnswer(ic -> {
+        lenient().when(anyUtils.getField(anyString())).thenAnswer(ic -> {
             String field = ic.getArgument(0);
             return FIELDS.get(anyUtils.anyTypeKind()).contains(field)
                     ? ReflectionUtils.findField(getClass(), "anyUtils")
                     : null;
         });
-        when(plainSchemaDAO.find(anyString())).thenAnswer(ic -> {
+        lenient().when(plainSchemaDAO.find(anyString())).thenAnswer(ic -> {
             String schemaName = ic.getArgument(0);
             switch (schemaName) {
                 case "email":
                 case "firstname":
                 case "location":
                     PlainSchema schema = mock(PlainSchema.class);
-                    when(schema.getKey()).thenReturn(schemaName);
-                    when(schema.getType()).thenReturn(AttrSchemaType.String);
+                    lenient().when(schema.getKey()).thenReturn(schemaName);
+                    lenient().when(schema.getType()).thenReturn(AttrSchemaType.String);
                     return schema;
 
                 default:
                     return null;
             }
         });
-        when(derSchemaDAO.find(anyString())).thenAnswer(ic -> {
+        lenient().when(derSchemaDAO.find(anyString())).thenAnswer(ic -> {
             String schemaName = ic.getArgument(0);
             switch (schemaName) {
                 case "cn":
                     DerSchema schema = mock(DerSchema.class);
-                    when(schema.getKey()).thenReturn(ic.getArgument(0));
+                    lenient().when(schema.getKey()).thenReturn(ic.getArgument(0));
                     return schema;
 
                 default:
                     return null;
             }
         });
-        when(virSchemaDAO.find(anyString())).thenAnswer(ic -> {
+        lenient().when(virSchemaDAO.find(anyString())).thenAnswer(ic -> {
             String schemaName = ic.getArgument(0);
             switch (schemaName) {
                 case "rvirtualdata":
                     VirSchema schema = mock(VirSchema.class);
-                    when(schema.getKey()).thenReturn(ic.getArgument(0));
+                    lenient().when(schema.getKey()).thenReturn(ic.getArgument(0));
                     return schema;
 
                 default:
@@ -190,6 +184,20 @@ public class IntAttrNameParserTest {
         assertNotNull(intAttrName);
         assertEquals(AnyTypeKind.USER, intAttrName.getAnyTypeKind());
         assertNull(intAttrName.getField());
+
+        Object nullObj = null;
+        int expected = new HashCodeBuilder().
+                append(AnyTypeKind.USER).append(nullObj).append(nullObj).append(nullObj).append(nullObj).
+                append(nullObj).append(nullObj).append(nullObj).append(nullObj).append(nullObj).append(nullObj).
+                build();
+        assertEquals(expected, intAttrName.hashCode());
+        IntAttrName intAttrName2 = intAttrNameParser.parse("email", AnyTypeKind.USER);
+        assertFalse(intAttrName.equals(intAttrName2));
+        assertFalse(intAttrName.equals(nullObj));
+        assertTrue(intAttrName.equals(intAttrName));
+        String toString = intAttrName.toString();
+        assertTrue(toString.startsWith("org.apache.syncope.core.provisioning.api.IntAttrName"));
+        assertTrue(toString.endsWith("[USER,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>,<null>]"));
     }
 
     @Test
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java
new file mode 100644
index 0000000..5671b32
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.springframework.test.util.ReflectionTestUtils;
+
+public class PropagationByResourceTest extends AbstractTest {
+
+    private final String key = "testKey";
+
+    private final PropagationByResource<String> propagationByResource = new PropagationByResource<>();
+
+    @Test
+    public void voidMethods(
+            @Mock Set<String> toBeCreated,
+            @Mock Set<String> toBeUpdated,
+            @Mock Set<String> toBeDeleted,
+            @Mock Map<String, String> oldConnObjectKeys,
+            @Mock PropagationByResource<String> propByRes) {
+        ReflectionTestUtils.setField(propagationByResource, "toBeCreated", toBeCreated);
+        ReflectionTestUtils.setField(propagationByResource, "toBeUpdated", toBeUpdated);
+        ReflectionTestUtils.setField(propagationByResource, "toBeDeleted", toBeDeleted);
+        ReflectionTestUtils.setField(propagationByResource, "oldConnObjectKeys", oldConnObjectKeys);
+
+        propagationByResource.purge();
+        verify(toBeCreated).removeAll(toBeDeleted);
+        verify(toBeCreated).removeAll(toBeUpdated);
+        verify(toBeUpdated).removeAll(toBeDeleted);
+
+        propagationByResource.clear();
+        verify(toBeCreated).clear();
+        verify(toBeCreated).clear();
+        verify(toBeUpdated).clear();
+
+        propagationByResource.merge(null);
+        verify(toBeCreated, times(0)).addAll(any());
+        verify(toBeUpdated, times(0)).addAll(any());
+        verify(toBeDeleted, times(0)).addAll(any());
+        verify(oldConnObjectKeys, times(0)).putAll(any());
+
+        propagationByResource.merge(propByRes);
+        verify(toBeCreated).addAll(any());
+        verify(toBeUpdated).addAll(any());
+        verify(toBeDeleted).addAll(any());
+
+        String oldConnObjectKey = "oldConnObjectKey";
+        propagationByResource.addOldConnObjectKey(key, oldConnObjectKey);
+        verify(oldConnObjectKeys).put(key, oldConnObjectKey);
+        propagationByResource.addOldConnObjectKey(key, null);
+        verify(oldConnObjectKeys, times(0)).put(key, null);
+        propagationByResource.addOldConnObjectKey(null, null);
+        verify(oldConnObjectKeys, times(0)).put(null, null);
+    }
+
+    @Test
+    public void add() {
+        assertTrue(propagationByResource.add(ResourceOperation.CREATE, key));
+        assertTrue(propagationByResource.add(ResourceOperation.UPDATE, key));
+        assertTrue(propagationByResource.add(ResourceOperation.DELETE, key));
+        assertFalse(propagationByResource.add(ResourceOperation.NONE, key));
+    }
+
+    @Test
+    public void addAll() {
+        List<String> keys = new ArrayList<>();
+        keys.add("testKey1");
+        keys.add("testKey2");
+
+        assertTrue(propagationByResource.addAll(ResourceOperation.CREATE, keys));
+        assertTrue(propagationByResource.addAll(ResourceOperation.UPDATE, keys));
+        assertTrue(propagationByResource.addAll(ResourceOperation.DELETE, keys));
+        assertFalse(propagationByResource.addAll(ResourceOperation.NONE, keys));
+    }
+
+    @Test
+    public void remove() {
+        assertFalse(propagationByResource.remove(ResourceOperation.CREATE, key));
+        assertFalse(propagationByResource.remove(ResourceOperation.UPDATE, key));
+        assertFalse(propagationByResource.remove(ResourceOperation.DELETE, key));
+        assertFalse(propagationByResource.remove(ResourceOperation.NONE, key));
+    }
+
+    @Test
+    public void removeAll() {
+        Set<String> keys = new HashSet<>();
+        keys.add("testKey1");
+        keys.add("testKey2");
+
+        assertFalse(propagationByResource.removeAll(ResourceOperation.CREATE, keys));
+        assertFalse(propagationByResource.removeAll(ResourceOperation.UPDATE, keys));
+        assertFalse(propagationByResource.removeAll(ResourceOperation.DELETE, keys));
+        assertFalse(propagationByResource.removeAll(ResourceOperation.NONE, keys));
+    }
+
+    @Test
+    public void removeAndRetainAll() {
+        List<String> keys = new ArrayList<>();
+        keys.add("testKey1");
+        keys.add("testKey2");
+
+        assertFalse(propagationByResource.removeAll(keys));
+        assertFalse(propagationByResource.retainAll(keys));
+    }
+
+    @Test
+    public void contains() {
+        assertFalse(propagationByResource.contains(ResourceOperation.CREATE, key));
+        assertFalse(propagationByResource.contains(ResourceOperation.UPDATE, key));
+        assertFalse(propagationByResource.contains(ResourceOperation.DELETE, key));
+        assertFalse(propagationByResource.contains(ResourceOperation.NONE, key));
+
+        Set<String> matchingList = new HashSet<>();
+        matchingList.add(key);
+        assertFalse(propagationByResource.contains(key));
+
+        ReflectionTestUtils.setField(propagationByResource, "toBeDeleted", matchingList);
+        assertTrue(propagationByResource.contains(key));
+    }
+
+    @Test
+    public void get() {
+        Set<String> matchingList = new HashSet<>();
+        matchingList.add(key);
+
+        ReflectionTestUtils.setField(propagationByResource, "toBeDeleted", matchingList);
+        assertEquals(matchingList, propagationByResource.get(ResourceOperation.DELETE));
+        assertEquals(Collections.<String>emptySet(), propagationByResource.get(ResourceOperation.CREATE));
+        assertEquals(Collections.<String>emptySet(), propagationByResource.get(ResourceOperation.UPDATE));
+        assertEquals(Collections.<String>emptySet(), propagationByResource.get(ResourceOperation.NONE));
+
+    }
+
+    @Test
+    public void asMap() {
+        assertEquals(Collections.emptyMap(), propagationByResource.asMap());
+    }
+
+    @Test
+    public void set() {
+        Set<String> keys = new HashSet<>();
+        keys.add("testKey1");
+        keys.add("testKey2");
+
+        propagationByResource.set(ResourceOperation.CREATE, Collections.<String>emptySet());
+        assertEquals(Collections.emptySet(), ReflectionTestUtils.getField(propagationByResource, "toBeCreated"));
+        propagationByResource.set(ResourceOperation.CREATE, keys);
+        assertEquals(keys, ReflectionTestUtils.getField(propagationByResource, "toBeCreated"));
+        propagationByResource.set(ResourceOperation.UPDATE, keys);
+        assertEquals(keys, ReflectionTestUtils.getField(propagationByResource, "toBeUpdated"));
+        propagationByResource.set(ResourceOperation.DELETE, keys);
+        assertEquals(keys, ReflectionTestUtils.getField(propagationByResource, "toBeDeleted"));
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/UserWorkflowResultTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/UserWorkflowResultTest.java
new file mode 100644
index 0000000..bf2abfc
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/UserWorkflowResultTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.commons.lang3.tuple.Pair;
+import org.junit.jupiter.api.Test;
+
+public class UserWorkflowResultTest extends AbstractTest {
+
+    @Test
+    public void test() {
+        PropagationByResource<String> propByRes = new PropagationByResource<>();
+        PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
+        UserWorkflowResult<String> userWorkflowResult;
+        UserWorkflowResult<String> userWorkflowResult2;
+        String result = "true";
+        String performedTask = "testTask";
+        Set<String> performedTasks = new HashSet<>();
+        performedTasks.add("testTask1");
+        performedTasks.add("testTask2");
+        performedTasks.add("testTask3");
+        Object nullObj = null;
+
+        userWorkflowResult = new UserWorkflowResult<>(result, propByRes, propByLinkedAccount, performedTask);
+        userWorkflowResult2 = new UserWorkflowResult<>(result, propByRes, propByLinkedAccount, performedTasks);
+
+        assertNotEquals(userWorkflowResult.hashCode(), userWorkflowResult2.hashCode());
+        assertFalse(userWorkflowResult.equals(Object.class));
+        assertFalse(userWorkflowResult.equals(nullObj));
+        assertTrue(userWorkflowResult.equals(userWorkflowResult2));
+        assertTrue(userWorkflowResult2.equals(userWorkflowResult2));
+        assertNotEquals(userWorkflowResult.toString(), userWorkflowResult2.toString());
+        assertEquals(performedTasks, userWorkflowResult2.getPerformedTasks());
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/WorkflowResultTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/WorkflowResultTest.java
new file mode 100644
index 0000000..7a4663a
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/WorkflowResultTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.syncope.core.provisioning.api;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+public class WorkflowResultTest extends AbstractTest {
+    
+    @Test
+    public void test(@Mock PropagationByResource<String> propByRes) {
+        String result = "result";
+        Set<String> performedTasks = new HashSet<>();
+        performedTasks.add("TEST");
+        WorkflowResult<String> workflowResult = new WorkflowResult<>(result, propByRes, performedTasks);
+        WorkflowResult<String> workflowResult2 = new WorkflowResult<>(result, propByRes, performedTasks);
+        
+        assertTrue(workflowResult.equals(workflowResult));
+        assertTrue(workflowResult.equals(workflowResult2));
+        assertFalse(workflowResult.equals(null));
+        assertFalse(workflowResult.equals(String.class));
+        
+        result = "newResult";
+        workflowResult.setResult(result);
+        assertEquals(result, workflowResult.getResult());
+        
+        assertEquals(propByRes, workflowResult2.getPropByRes());
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheKeyTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheKeyTest.java
new file mode 100644
index 0000000..8004c16
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheKeyTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.cache;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.junit.jupiter.api.Test;
+
+public class VirAttrCacheKeyTest extends AbstractTest {
+
+    @Test
+    public void test() {
+        String type = "type";
+        String key = "key";
+        String virSchema = "virSchema";
+        VirAttrCacheKey cacheKey = new VirAttrCacheKey(type, key, virSchema);
+        VirAttrCacheKey cacheKey2 = new VirAttrCacheKey(type, key, virSchema);
+        VirAttrCacheKey cacheKey3 = new VirAttrCacheKey(type, String.format(type, "3"), String.format(virSchema, "3"));
+        Object nullObj = null;
+
+        assertEquals(type, cacheKey.getKind());
+        assertEquals(key, cacheKey.getKey());
+        assertEquals(virSchema, cacheKey.getVirSchema());
+
+        assertEquals(cacheKey.hashCode(), cacheKey2.hashCode());
+        assertFalse(cacheKey.equals(nullObj));
+        assertFalse(cacheKey.equals(String.class));
+        assertTrue(cacheKey.equals(cacheKey));
+        assertTrue(cacheKey.equals(cacheKey2));
+        assertFalse(cacheKey.equals(cacheKey3));
+
+        assertEquals(cacheKey.toString(), cacheKey2.toString());
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheValueTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheValueTest.java
new file mode 100644
index 0000000..c4ee199
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheValueTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.cache;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Collections;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.junit.jupiter.api.Test;
+
+public class VirAttrCacheValueTest extends AbstractTest {
+
+    @Test
+    public void test() {
+        Object nullObj = null;
+        VirAttrCacheValue cacheValue = new VirAttrCacheValue(Collections.singletonList("testValue"));
+        VirAttrCacheValue cacheValue2 = new VirAttrCacheValue(Collections.emptyList());
+
+        cacheValue.forceExpiring();
+        cacheValue2.forceExpiring();
+        assertEquals(cacheValue.getCreationDate(), cacheValue2.getCreationDate());
+        assertNotEquals(cacheValue.getValues(), cacheValue2.getValues());
+        assertEquals(cacheValue.getLastAccessDate(), cacheValue2.getLastAccessDate());
+        assertNotEquals(cacheValue.hashCode(), cacheValue2.hashCode());
+        assertFalse(cacheValue.equals(cacheValue2));
+        assertTrue(cacheValue.equals(cacheValue));
+        assertFalse(cacheValue2.equals(nullObj));
+        assertFalse(cacheValue2.equals(String.class));
+        assertNotEquals(cacheValue.toString(), cacheValue2.toString());
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEventTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEventTest.java
new file mode 100644
index 0000000..547ac7a
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEventTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.event;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.UUID;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.junit.jupiter.api.Test;
+
+public class AfterHandlingEventTest extends AbstractTest {
+
+    @Test
+    public void test() {
+     String who = "testUser";
+        AuditElements.EventCategoryType type = AuditElements.EventCategoryType.CUSTOM;
+        String category = SyncopeConstants.REALM_ANYTYPE.toLowerCase();
+        String subcategory = UUID.randomUUID().toString();
+        String event = "testEvent";
+        AuditElements.Result condition = AuditElements.Result.SUCCESS;
+        Object before = "before";
+        Object output = "output";
+        Object[] input = new String[] { "value1", "value2" };
+        AfterHandlingEvent afterHandlingEvent = new AfterHandlingEvent(
+                who,
+                type,
+                category,
+                subcategory, 
+                event,
+                condition,
+                before, 
+                output, 
+                input);
+        
+        assertEquals(who, afterHandlingEvent.getWho());
+        assertEquals(type, afterHandlingEvent.getType());
+        assertEquals(category, afterHandlingEvent.getCategory());
+        assertEquals(subcategory, afterHandlingEvent.getSubcategory());
+        assertEquals(event, afterHandlingEvent.getEvent());
+        assertEquals(condition, afterHandlingEvent.getCondition());
+        assertEquals(before, afterHandlingEvent.getBefore());
+        assertEquals(output, afterHandlingEvent.getOutput());
+        assertEquals(input, afterHandlingEvent.getInput());
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java
new file mode 100644
index 0000000..4d8a878
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.job;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.util.UUID;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.syncope.core.persistence.api.entity.Report;
+import org.apache.syncope.core.persistence.api.entity.task.Task;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+
+public class JobNamerTest extends AbstractTest {
+
+    private String name;
+
+    @Test
+    public void getTaskKeyFromJobName() {
+        name = "testName";
+        assertNull(JobNamer.getTaskKeyFromJobName(name));
+
+        String uuid = UUID.randomUUID().toString();
+        name = String.format("taskJob%s", uuid);
+        assertEquals(uuid, JobNamer.getTaskKeyFromJobName(name));
+    }
+
+    @Test
+    public void getReportKeyFromJobName() {
+        name = "testName";
+        assertNull(JobNamer.getTaskKeyFromJobName(name));
+
+        String uuid = UUID.randomUUID().toString();
+        name = String.format("reportJob%s", uuid);
+        assertEquals(uuid, JobNamer.getReportKeyFromJobName(name));
+    }
+
+    @Test
+    public void getJobKey(@Mock Task task) {
+        String uuid = UUID.randomUUID().toString();
+        when(task.getKey()).thenReturn(uuid);
+        assertTrue(EqualsBuilder.reflectionEquals(new JobKey("taskJob" + task.getKey(), Scheduler.DEFAULT_GROUP),
+                JobNamer.getJobKey(task)));
+    }
+    
+    @Test
+    public void getJobKey(@Mock Report report) {
+        String uuid = UUID.randomUUID().toString();
+        when(report.getKey()).thenReturn(uuid);
+        assertTrue(EqualsBuilder.reflectionEquals(new JobKey("reportJob" + report.getKey(), Scheduler.DEFAULT_GROUP),
+                JobNamer.getJobKey(report)));
+    }
+    
+    @Test
+    public void getTriggerName() {
+        String jobName = "testJobName";
+        assertEquals("Trigger_" + jobName, JobNamer.getTriggerName(jobName));
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfoTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfoTest.java
new file mode 100644
index 0000000..8e34e32
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfoTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.propagation;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Optional;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+public class PropagationTaskInfoTest extends AbstractTest {
+
+    @Mock
+    private ExternalResource externalResource;
+
+    @Mock
+    private transient Connector connector;
+
+    @InjectMocks
+    private PropagationTaskInfo propagationTaskInfo;
+
+    @Test
+    public void test(@Mock Optional<ConnectorObject> beforeObj) {
+        PropagationTaskInfo propagationTaskInfo2 = new PropagationTaskInfo(externalResource);
+        Object nullObj = null;
+
+        assertTrue(propagationTaskInfo2.equals(propagationTaskInfo2));
+        assertTrue(propagationTaskInfo2.equals(propagationTaskInfo));
+        assertFalse(propagationTaskInfo.equals(nullObj));
+        assertFalse(propagationTaskInfo.equals(String.class));
+        assertEquals(propagationTaskInfo.hashCode(), propagationTaskInfo2.hashCode());
+        assertEquals(connector, propagationTaskInfo.getConnector());
+
+        propagationTaskInfo2.setConnector(connector);
+        assertEquals(connector, propagationTaskInfo2.getConnector());
+        assertEquals(externalResource.getClass(), propagationTaskInfo.getExternalResource().getClass());
+
+        IllegalArgumentException exception =
+                assertThrows(IllegalArgumentException.class, () -> propagationTaskInfo.setResource("testResource"));
+        assertEquals(exception.getClass(), IllegalArgumentException.class);
+        assertNull(propagationTaskInfo2.getResource());
+        
+        propagationTaskInfo.setBeforeObj(beforeObj);
+        assertEquals(beforeObj, propagationTaskInfo.getBeforeObj());
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java
new file mode 100644
index 0000000..3220d0f
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.pushpull;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import java.util.ArrayList;
+import org.apache.syncope.common.lib.types.ConflictResolutionAction;
+import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+public class ProvisioningProfileTest extends AbstractTest {
+
+
+    @Test
+    public void test(
+            @Mock Connector connector,
+            @Mock PushTask pushTask) {
+        boolean dryRun = false;
+        ConflictResolutionAction conflictResolutionAction = ConflictResolutionAction.FIRSTMATCH;
+        ProvisioningProfile<PushTask, PushActions> profile;
+        profile = new ProvisioningProfile<>(connector, pushTask);
+
+        assertEquals(connector, profile.getConnector());
+        assertEquals(pushTask, profile.getTask());
+        assertEquals(new ArrayList<>(), profile.getResults());
+        assertEquals(new ArrayList<>(), profile.getActions());
+
+        profile.setDryRun(dryRun);
+        assertFalse(profile.isDryRun());
+
+        profile.setConflictResolutionAction(conflictResolutionAction);
+        assertEquals(conflictResolutionAction, profile.getConflictResolutionAction());
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java
new file mode 100644
index 0000000..4d8c578
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.serialization;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.util.Collections;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+public class AttributeDeserializerTest extends AbstractTest {
+
+    @Mock
+    private JsonParser jp;
+
+    @Mock
+    private DeserializationContext ct;
+
+    @Mock
+    private JsonNode node;
+
+    @Mock
+    private JsonNode node2;
+
+    @Mock
+    private ObjectNode tree;
+
+    private final AttributeDeserializer deserializer = new AttributeDeserializer();
+
+    private String name;
+
+    private Attribute attr;
+
+    @BeforeEach
+    public void initTest() throws IOException {
+        name = "__NAME__";
+        when(jp.readValueAsTree()).thenReturn(tree);
+        when(tree.get("name")).thenReturn(node2);
+        when(tree.get("value")).thenReturn(node);
+        when(node.iterator()).thenReturn(Collections.singletonList(node).iterator());
+    }
+
+    @Test
+    public void deserializeIsNull() throws IOException {
+        when(node2.asText()).thenReturn(name);
+        when(node.isNull()).thenReturn(Boolean.TRUE);
+        attr = deserializer.deserialize(jp, ct);
+        assertEquals(name, attr.getName());
+        assertEquals(Collections.singletonList(null), attr.getValue());
+    }
+
+    @Test
+    public void deserializeIsBoolean() throws IOException {
+        when(node2.asText()).thenReturn(name);
+        when(node.isBoolean()).thenReturn(Boolean.TRUE);
+        when(node.asBoolean()).thenReturn(Boolean.TRUE);
+        attr = deserializer.deserialize(jp, ct);
+        assertEquals(name, attr.getName());
+        assertEquals(Collections.singletonList(Boolean.TRUE.toString()).get(0), attr.getValue().get(0));
+    }
+
+    @Test
+    public void deserializeIsDouble() throws IOException {
+        Double number = 9000.1;
+        name = "__TEST__";
+        when(node2.asText()).thenReturn(name);
+        when(node.isDouble()).thenReturn(Boolean.TRUE);
+        when(node.asDouble()).thenReturn(number);
+        attr = deserializer.deserialize(jp, ct);
+        assertEquals(name, attr.getName());
+        assertEquals(Collections.singletonList(number).get(0), attr.getValue().get(0));
+    }
+
+    @Test
+    public void deserializeIsLong() throws IOException {
+        Long number = 9000L;
+        name = "__UID__";
+        when(node2.asText()).thenReturn(name);
+        when(node.isLong()).thenReturn(Boolean.TRUE);
+        when(node.asLong()).thenReturn(number);
+        attr = deserializer.deserialize(jp, ct);
+        assertEquals(name, attr.getName());
+        assertEquals(Collections.singletonList(number.toString()).get(0), attr.getValue().get(0));
+    }
+
+    @Test
+    public void deserializeIsInt() throws IOException {
+        Integer number = 9000;
+        when(node2.asText()).thenReturn(name);
+        when(node.isInt()).thenReturn(Boolean.TRUE);
+        when(node.asInt()).thenReturn(number);
+        attr = deserializer.deserialize(jp, ct);
+        assertEquals(attr.getName(), name);
+        assertEquals(Collections.singletonList(number.toString()).get(0), attr.getValue().get(0));
+    }
+
+    @Test
+    public void deserializeIsText() throws IOException {
+        String text = "<binary>test";
+        when(node2.asText()).thenReturn(name);
+        when(node.asText()).thenReturn(text);
+        attr = deserializer.deserialize(jp, ct);
+        assertEquals(attr.getName(), name);
+        assertEquals(Collections.singletonList(text).get(0), attr.getValue().get(0));
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java
new file mode 100644
index 0000000..36f25a4
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.serialization;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyDouble;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import java.io.IOException;
+import java.util.Collections;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.identityconnectors.common.security.GuardedString;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+public class AttributeSerializerTest extends AbstractTest {
+
+    @Test
+    public void serialize(@Mock Attribute source, @Mock JsonGenerator jgen, @Mock SerializerProvider sp)
+            throws IOException {
+        AttributeSerializer serializer = new AttributeSerializer();
+        when(source.getValue()).thenReturn(null);
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeStartObject();
+        verify(jgen).writeFieldName("value");
+        verify(jgen).writeNull();
+
+        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(new GuardedString()));
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeObject(any(GuardedString.class));
+
+        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(9000));
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeNumber(anyInt());
+
+        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(9000L));
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeNumber(anyLong());
+
+        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(9000.1));
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeNumber(anyDouble());
+
+        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(Boolean.TRUE));
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeBoolean(anyBoolean());
+
+        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(new byte[] { 9, 0, 0, 0 }));
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeString(anyString());
+
+        when(source.getValue()).thenAnswer(ic -> Collections.singletonList("test"));
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeString(eq("test"));
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java
new file mode 100644
index 0000000..8004ca3
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.serialization;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.identityconnectors.common.security.EncryptorFactory;
+import org.identityconnectors.common.security.GuardedString;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.springframework.test.util.ReflectionTestUtils;
+
+public class GuardedStringDeserializerTest extends AbstractTest {
+
+    private static final String READONLY = "readOnly";
+
+    private static final String DISPOSED = "disposed";
+
+    private static final String ENCRYPTED_BYTES = "encryptedBytes";
+
+    private static final String BASE64_SHA1_HASH = "base64SHA1Hash";
+
+    private final GuardedStringDeserializer deserializer = new GuardedStringDeserializer();
+
+    @Mock
+    private JsonParser jp;
+
+    @Mock
+    private DeserializationContext ctx;
+
+    @Mock
+    private JsonNode node;
+
+    @Test
+    public void deserialize() throws IOException {
+        Map<String, JsonNode> kids = new HashMap<>();
+        kids.put(READONLY, node);
+        kids.put(DISPOSED, node);
+        kids.put(ENCRYPTED_BYTES, node);
+        kids.put(BASE64_SHA1_HASH, node);
+        ObjectNode tree = new ObjectNode(JsonNodeFactory.instance, kids);
+        String testString = "randomTestString";
+        byte[] encryptedBytes = EncryptorFactory.getInstance().getDefaultEncryptor().encrypt(testString.getBytes());
+        String encryptedString = Base64.getEncoder().encodeToString(encryptedBytes);
+
+        when(jp.readValueAsTree()).thenReturn(tree);
+        when(node.asText()).thenReturn(encryptedString);
+        assertEquals(Boolean.FALSE, ReflectionTestUtils.getField(deserializer.deserialize(jp, ctx), READONLY));
+        kids.remove(READONLY);
+        assertEquals(Boolean.FALSE, ReflectionTestUtils.getField(deserializer.deserialize(jp, ctx), DISPOSED));
+        kids.remove(DISPOSED);
+        assertEquals(encryptedString, 
+                ReflectionTestUtils.getField(deserializer.deserialize(jp, ctx), BASE64_SHA1_HASH));
+
+        kids.remove(BASE64_SHA1_HASH);
+        GuardedString expected = new GuardedString(new String(testString.getBytes()).toCharArray());
+        assertTrue(EqualsBuilder.reflectionEquals(ReflectionTestUtils.getField(expected, ENCRYPTED_BYTES),
+                ReflectionTestUtils.getField(deserializer.deserialize(jp, ctx), ENCRYPTED_BYTES)));
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java
new file mode 100644
index 0000000..eca9489
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.serialization;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import java.io.IOException;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.identityconnectors.common.security.GuardedString;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+public class GuardedStringSerializerTest extends AbstractTest {
+
+    private static final String READONLY = "readOnly";
+
+    private static final String DISPOSED = "disposed";
+
+    private static final String ENCRYPTED_BYTES = "encryptedBytes";
+
+    private static final String BASE64_SHA1_HASH = "base64SHA1Hash";
+    
+    private final GuardedStringSerializer serializer = new GuardedStringSerializer();
+    
+    @Test
+    public void serialize(
+            @Mock JsonGenerator jgen, 
+            @Mock SerializerProvider sp) throws IOException {
+        serializer.serialize(new GuardedString(), jgen, sp);
+        verify(jgen).writeBooleanField(READONLY, false);
+        verify(jgen).writeBooleanField(DISPOSED, false);
+        verify(jgen).writeStringField(eq(ENCRYPTED_BYTES), anyString());
+        verify(jgen).writeStringField(eq(BASE64_SHA1_HASH), anyString());
+        verify(jgen).writeEndObject();
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java
new file mode 100644
index 0000000..d94a87e
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.serialization;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+public class POJOHelperTest extends AbstractTest {
+
+    @Test
+    public void serialize() {
+        Object object = 9001;
+
+        assertEquals(String.valueOf(object), POJOHelper.serialize(object));
+    }
+
+    @Test
+    public void serializeWithDefaultPrettyPrinter() {
+        Object object = 9001;
+
+        assertEquals(String.valueOf(object), POJOHelper.serializeWithDefaultPrettyPrinter(object));
+    }
+
+    @Test
+    public void deserializeWithClassReference() {
+        String serialized = "false";
+
+        assertEquals(Boolean.valueOf(serialized), POJOHelper.deserialize(serialized, Object.class));
+    }
+
+    @Test
+    public void deserializeWithTypeReference(@Mock TypeReference<? extends Object> reference) {
+        String serialized = "false";
+
+        assertNull(POJOHelper.deserialize(serialized, reference));
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java
new file mode 100644
index 0000000..59ff396
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.serialization;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+public class SyncTokenDeserializerTest extends AbstractTest {
+
+    @Mock
+    private JsonParser jp;
+
+    @Mock
+    private DeserializationContext ct;
+
+    @Mock
+    private JsonNode node;
+
+    @Mock
+    private ObjectNode tree;
+
+    private final SyncTokenDeserializer deserializer = new SyncTokenDeserializer();
+
+    @BeforeEach
+    public void initTest() throws IOException {
+        when(jp.readValueAsTree()).thenReturn(tree);
+        when(tree.has("value")).thenReturn(Boolean.TRUE);
+        when(tree.get("value")).thenReturn(node);
+    }
+
+    @Test
+    public void deserializeIsBoolean() throws IOException {
+        Boolean value = Boolean.TRUE;
+        when(node.isBoolean()).thenReturn(value);
+        when(node.asBoolean()).thenReturn(value);
+        assertEquals(value, deserializer.deserialize(jp, ct).getValue());
+    }
+
+    @Test
+    public void deserializeIsDouble() throws IOException {
+        Double value = 9000.1;
+        when(node.isDouble()).thenReturn(Boolean.TRUE);
+        when(node.asDouble()).thenReturn(value);
+        assertEquals(value, deserializer.deserialize(jp, ct).getValue());
+    }
+
+    @Test
+    public void deserializeIsLong() throws IOException {
+        Long value = 9000L;
+        when(node.isLong()).thenReturn(Boolean.TRUE);
+        when(node.asLong()).thenReturn(value);
+        assertEquals(value, deserializer.deserialize(jp, ct).getValue());
+    }
+
+    @Test
+    public void deserializeIsInt() throws IOException {
+        Integer value = 9000;
+        when(node.isInt()).thenReturn(Boolean.TRUE);
+        when(node.asInt()).thenReturn(value);
+        assertEquals(value, deserializer.deserialize(jp, ct).getValue());
+    }
+
+    @Test
+    public void deserializeIsString() throws IOException {
+        String value = "testValue";
+        when(node.asText()).thenReturn(value);
+        assertEquals(value, deserializer.deserialize(jp, ct).getValue());
+
+        value = Base64.getEncoder().encodeToString(value.getBytes(StandardCharsets.ISO_8859_1));
+        when(node.asText()).thenReturn(value);
+        assertTrue(EqualsBuilder.reflectionEquals(Base64.getDecoder().decode(value),
+                deserializer.deserialize(jp, ct).getValue()));
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java
new file mode 100644
index 0000000..8423668
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.serialization;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import java.io.IOException;
+import java.util.UUID;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.identityconnectors.framework.common.objects.SyncToken;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+public class SyncTokenSerializerTest extends AbstractTest {
+
+    @Test
+    public void SyncTokenSerializer(
+            @Mock JsonGenerator jgen,
+            @Mock SerializerProvider sp) throws IOException {
+        SyncTokenSerializer serializer = new SyncTokenSerializer();
+        SyncToken source = new SyncToken(UUID.randomUUID().toString());
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeStartObject();
+        verify(jgen).writeFieldName("value");
+        verify(jgen).writeEndObject();
+
+        boolean bool = false;
+        source = new SyncToken(bool);
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeBoolean(bool);
+
+        double doubleNum = 9000.1;
+        source = new SyncToken(doubleNum);
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeNumber(doubleNum);
+
+        long longNum = 9001;
+        source = new SyncToken(longNum);
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeNumber(longNum);
+
+        int intNum = 9000;
+        source = new SyncToken(intNum);
+        serializer.serialize(source, jgen, sp);
+        verify(jgen).writeNumber(intNum);
+
+        byte[] bytes = new byte[] { 9, 0, 0, 1 };
+        source = new SyncToken(bytes);
+        serializer.serialize(source, jgen, sp);
+        verify(jgen, times(2)).writeString(anyString());
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/ConnPoolConfUtilsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/ConnPoolConfUtilsTest.java
new file mode 100644
index 0000000..12f77bd
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/ConnPoolConfUtilsTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.utils;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.verify;
+
+import org.apache.syncope.common.lib.to.ConnPoolConfTO;
+import org.apache.syncope.core.persistence.api.entity.ConnPoolConf;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.identityconnectors.common.pooling.ObjectPoolConfiguration;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+public class ConnPoolConfUtilsTest extends AbstractTest {
+
+    @Mock
+    private ConnPoolConf cpc;
+
+    @Test
+    public void getConnPoolConf() {
+        ConnPoolConfTO cpcto = new ConnPoolConfTO();
+        ConnPoolConfUtils.getConnPoolConf(cpcto, cpc);
+        verify(cpc).setMaxIdle(anyInt());
+        verify(cpc).setMaxObjects(anyInt());
+        verify(cpc).setMaxWait(anyLong());
+        verify(cpc).setMinEvictableIdleTimeMillis(anyLong());
+        verify(cpc).setMinIdle(anyInt());
+    }
+
+    @Test
+    public void updateObjectPoolConfiguration(@Mock ObjectPoolConfiguration opc) {
+        ConnPoolConfUtils.updateObjectPoolConfiguration(opc, cpc);
+        verify(opc).setMaxIdle(anyInt());
+        verify(opc).setMaxObjects(anyInt());
+        verify(opc).setMaxWait(anyLong());
+        verify(opc).setMinEvictableIdleTimeMillis(anyLong());
+        verify(opc).setMinIdle(anyInt());
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/FormatUtilsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/FormatUtilsTest.java
new file mode 100644
index 0000000..61c2657
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/FormatUtilsTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import org.apache.commons.lang3.time.DateUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.junit.jupiter.api.Test;
+
+public class FormatUtilsTest extends AbstractTest {
+
+    private final Calendar calendar = Calendar.getInstance();
+
+    private final Date date = calendar.getTime();
+
+    private String conversionPattern;
+
+    @Test
+    public void formatDate() {
+        assertEquals(new SimpleDateFormat(SyncopeConstants.DEFAULT_DATE_PATTERN).format(date),
+                FormatUtils.format(date));
+
+        conversionPattern = "dd/MM/yyyy";
+        assertEquals(new SimpleDateFormat(conversionPattern).format(date),
+                FormatUtils.format(date, false, conversionPattern));
+    }
+
+    @Test
+    public void formatLongNumber() {
+        long number = date.getTime();
+        DecimalFormat df = new DecimalFormat();
+        df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH));
+        assertEquals(df.format(number), FormatUtils.format(number));
+
+        conversionPattern = "###,###";
+        df = new DecimalFormat(conversionPattern);
+        df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH));
+        assertEquals(df.format(number), FormatUtils.format(number, conversionPattern));
+    }
+
+    @Test
+    public void formatDoubleNumber() {
+        double number = date.getTime();
+        DecimalFormat df = new DecimalFormat();
+        df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH));
+        assertEquals(df.format(number), FormatUtils.format(number));
+
+        conversionPattern = "###,###";
+        df = new DecimalFormat(conversionPattern);
+        df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH));
+        assertEquals(df.format(number), FormatUtils.format(number, conversionPattern));
+    }
+
+    @Test
+    public void parseDate() throws ParseException {
+        String source = new SimpleDateFormat(SyncopeConstants.DEFAULT_DATE_PATTERN).format(date);
+        assertEquals(DateUtils.parseDate(source, SyncopeConstants.DATE_PATTERNS),
+                FormatUtils.parseDate(source));
+
+        conversionPattern = "dd-MM-yyyy";
+        source = new SimpleDateFormat(conversionPattern).format(date);
+        assertEquals(DateUtils.parseDate(source, conversionPattern),
+                FormatUtils.parseDate(source, conversionPattern));
+    }
+
+    @Test
+    public void parseNumber() throws ParseException {
+        String source = String.valueOf(date.getTime());
+        conversionPattern = "###,###";
+        assertEquals(Long.valueOf(source), FormatUtils.parseNumber(source, conversionPattern));
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/JexlUtilsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/JexlUtilsTest.java
new file mode 100644
index 0000000..cb71c91
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/JexlUtilsTest.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.commons.jexl3.JexlContext;
+import org.apache.commons.jexl3.JxltEngine;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.RealmTO;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.apache.syncope.core.provisioning.api.DerAttrHandler;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+public class JexlUtilsTest extends AbstractTest {
+
+    @Mock
+    JexlContext context;
+
+    private String expression;
+
+    @Test
+    public void newJxltEngine() {
+        JxltEngine engine = JexlUtils.newJxltEngine();
+        assertNotNull(engine);
+    }
+
+    @Test
+    public void isExpressionValid() {
+        expression = "6 * 12 + 5 / 2.6";
+        assertTrue(JexlUtils.isExpressionValid(expression));
+
+        expression = "@inv4lid expression!";
+        assertFalse(JexlUtils.isExpressionValid(expression));
+    }
+
+    @Test
+    public void evaluate() {
+        String result = StringUtils.EMPTY;
+        assertEquals(result, JexlUtils.evaluate(expression, context));
+
+        expression = "6 * 12 + 5 / 2.6";
+        result = "73.92307692307692";
+        assertEquals(result, JexlUtils.evaluate(expression, context));
+    }
+
+    @Test
+    public void addFieldsToContext(
+            @Mock Any<?> any,
+            @Mock AnyTO anyTO,
+            @Mock Realm realm,
+            @Mock RealmTO realmTO) {
+        JexlUtils.addFieldsToContext(new Exception(), context);
+        verify(context, times(2)).set(eq("cause"), any());
+
+        String testFullPath = "testFullPath";
+        when(any.getRealm()).thenReturn(realm);
+        when(realm.getFullPath()).thenReturn(testFullPath);
+        JexlUtils.addFieldsToContext(any, context);
+        verify(context).set("realm", testFullPath);
+
+        String testRealm = "testRealm";
+        when(anyTO.getRealm()).thenReturn(testRealm);
+        JexlUtils.addFieldsToContext(anyTO, context);
+        verify(context, times(3)).set("realm", testRealm);
+
+        String fullPath = "test/full/path";
+        when(realm.getFullPath()).thenReturn(fullPath);
+        JexlUtils.addFieldsToContext(realm, context);
+        verify(context, times(2)).set("fullPath", fullPath);
+
+        fullPath = "test/full/path2";
+        when(realmTO.getFullPath()).thenReturn(fullPath);
+        JexlUtils.addFieldsToContext(realmTO, context);
+        verify(context, times(2)).set("fullPath", fullPath);
+    }
+
+    @Test
+    public void addAttrTOsToContext() {
+        String schemaName = "testSchema";
+        String value = "testValue";
+        Collection<Attr> attrs = new ArrayList<>();
+        Attr attr = new Attr.Builder(schemaName).build();
+        attrs.add(attr);
+
+        JexlUtils.addAttrsToContext(attrs, context);
+        verify(context).set(schemaName, StringUtils.EMPTY);
+
+        attr = new Attr.Builder(schemaName).value(value).build();
+        attrs.clear();
+        attrs.add(attr);
+
+        JexlUtils.addAttrsToContext(attrs, context);
+        verify(context).set(schemaName, value);
+    }
+
+    @Test
+    public void addPlainAttrsToContext(@Mock Collection<? extends PlainAttr<?>> attrs) {
+        JexlUtils.addPlainAttrsToContext(attrs, context);
+        verify(context, times(0)).set(anyString(), any());
+    }
+
+    @Test
+    public void addDerAttrsToContext(
+            @Mock DerAttrHandler derAttrHandler,
+            @Mock Any<?> any,
+            @Mock DerSchema derSchema) {
+        Map<DerSchema, String> derAttrs = new HashMap<>();
+        derAttrs.put(derSchema, expression);
+
+        when(derAttrHandler.getValues(any())).thenReturn(derAttrs);
+        JexlUtils.addDerAttrsToContext(any, derAttrHandler, context);
+        verify(context).set(derAttrs.get(derSchema), expression);
+    }
+
+    @Test
+    public void evaluateMandatoryCondition(
+            @Mock DerAttrHandler derAttrHandler,
+            @Mock Any<?> any,
+            @Mock DerSchema derSchema,
+            @Mock Collection<? extends PlainAttr<?>> plainAttrs) {
+        Map<DerSchema, String> derAttrs = new HashMap<>();
+        derAttrs.put(derSchema, expression);
+
+        when(any.getPlainAttrs()).thenReturn(new ArrayList<>());
+        when(derAttrHandler.getValues(any())).thenReturn(derAttrs);
+
+        assertTrue(JexlUtils.evaluateMandatoryCondition("true", any, derAttrHandler));
+        assertFalse(JexlUtils.evaluateMandatoryCondition("false", any, derAttrHandler));
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/RealmUtilsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/RealmUtilsTest.java
new file mode 100644
index 0000000..700dc95
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/RealmUtilsTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.junit.jupiter.api.Test;
+
+public class RealmUtilsTest extends AbstractTest {
+
+    @Test
+    public void getGroupOwnerRealm() {
+        String realmPath = "realmPath";
+        String groupKey = "groupKey";
+        assertEquals(realmPath + "@" + groupKey, RealmUtils.getGroupOwnerRealm(realmPath, groupKey));
+    }
+
+    @Test
+    public void normalizingAddTo() {
+        Set<String> realms = new HashSet<>();
+        realms.add("realm1");
+        realms.add("realm2");
+        String newRealm = "realm123";
+        assertFalse(RealmUtils.normalizingAddTo(realms, newRealm));
+        assertEquals(2, realms.size());
+
+        realms.clear();
+        realms.add("testRealm1");
+        realms.add("realm2");
+        newRealm = "test";
+        assertTrue(RealmUtils.normalizingAddTo(realms, newRealm));
+        assertEquals(2, realms.size());
+    }
+    
+    @Test
+    public void getEffective() {
+        Set<String> allowedRealms = new HashSet<>();
+        String requestedRealm = "requestedRealm";
+        allowedRealms.add("testRealm1");
+        allowedRealms.add("testRealm2");
+        allowedRealms.add("testRealm3");
+        allowedRealms.add("requestedRealm");
+        Set<String> effective = RealmUtils.getEffective(allowedRealms, requestedRealm);
+        assertEquals(allowedRealms, effective);
+    }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/URIUtilsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/URIUtilsTest.java
new file mode 100644
index 0000000..39db524
--- /dev/null
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/URIUtilsTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.syncope.core.provisioning.api.AbstractTest;
+import org.junit.jupiter.api.Test;
+
+public class URIUtilsTest extends AbstractTest {
+
+    @Test
+    public void buildForConnId() throws URISyntaxException, MalformedURLException {
+        AtomicReference<String> location = new AtomicReference<>();
+        location.set("www.tirasa.net");
+        IllegalArgumentException exception =
+                assertThrows(IllegalArgumentException.class, () -> URIUtils.buildForConnId(location.get()));
+        assertEquals(exception.getClass(), IllegalArgumentException.class);
+
+        location.set("connid:test/location");
+        URI expectedURI = new URI(location.get().trim());
+        assertEquals(expectedURI, URIUtils.buildForConnId(location.get()));
+    }
+}
diff --git a/core/provisioning-api/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/core/provisioning-api/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..5895d20
--- /dev/null
+++ b/core/provisioning-api/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+mock-maker-inline
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
index 9884a63..172e24c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
@@ -108,7 +108,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
 
     protected abstract String getName(AnyCR anyCR);
 
-    protected abstract ProvisioningManager<?, ?, ?> getProvisioningManager();
+    protected abstract ProvisioningManager<?, ?> getProvisioningManager();
 
     protected abstract AnyTO doCreate(AnyCR anyCR, SyncDelta delta);
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java
index 14943a0..765495b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java
@@ -63,7 +63,7 @@ public class DefaultAnyObjectPullResultHandler extends AbstractPullResultHandler
     }
 
     @Override
-    protected ProvisioningManager<?, ?, ?> getProvisioningManager() {
+    protected ProvisioningManager<?, ?> getProvisioningManager() {
         return anyObjectProvisioningManager;
     }
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java
index 63ba2c0..538b786 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java
@@ -73,7 +73,7 @@ public class DefaultGroupPullResultHandler extends AbstractPullResultHandler imp
     }
 
     @Override
-    protected ProvisioningManager<?, ?, ?> getProvisioningManager() {
+    protected ProvisioningManager<?, ?> getProvisioningManager() {
         return groupProvisioningManager;
     }
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
index facde19..3bf70cf 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
@@ -88,7 +88,7 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl
     }
 
     @Override
-    protected ProvisioningManager<?, ?, ?> getProvisioningManager() {
+    protected ProvisioningManager<?, ?> getProvisioningManager() {
         return userProvisioningManager;
     }
 
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java
index 01f7a55..cd5372e 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java
@@ -117,6 +117,11 @@ public class DBPasswordPullActionsTest extends AbstractTest {
         connConfProperty.getValues().add(digest);
 
         dBPasswordPullActions.beforeProvision(profile, syncDelta, userCR);
+        userTO.setPassword(password);
+        connConfProperty.getValues().clear();
+        connConfProperty.getValues().add(digest);
+
+        dBPasswordPullActions.beforeProvision(profile, syncDelta, userCR);
 
         assertEquals(CipherAlgorithm.valueOf(digest), ReflectionTestUtils.getField(dBPasswordPullActions, "cipher"));
         assertEquals(password, ReflectionTestUtils.getField(dBPasswordPullActions, "encodedPassword"));
@@ -129,7 +134,10 @@ public class DBPasswordPullActionsTest extends AbstractTest {
                 build();
 
         dBPasswordPullActions.beforeUpdate(profile, syncDelta, userTO, userUR);
+        userUR = new UserUR();
+        userUR.setPassword(new PasswordPatch.Builder().value("an0therTestP4ss").build());
 
+        dBPasswordPullActions.beforeUpdate(profile, syncDelta, userTO, userUR);
         assertEquals(cipher, ReflectionTestUtils.getField(dBPasswordPullActions, "cipher"));
         assertEquals(encodedPassword, ReflectionTestUtils.getField(dBPasswordPullActions, "encodedPassword"));
     }