diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..28a9a9c
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,29 @@
+name: CI
+on:
+  pull_request:
+  push:
+    branches:
+      - master
+jobs:
+  tests:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        php: ['7.4', '8.0', '8.1', '8.2']
+
+    name: PHP ${{ matrix.php }} tests
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.php }}
+          coverage: none
+
+      - uses: "ramsey/composer-install@v1"
+        with:
+          composer-options: "--prefer-dist"
+
+      - run: vendor/bin/phpunit --exclude-group=functional
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index a478056..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-sudo: false
-
-git:
-    depth: 10
-
-language: php
-
-php:
-    - '5.6'
-    - '7.0'
-
-cache:
-    directories:
-        - $HOME/.composer/cache
-
-install:
-    - composer self-update
-    - travis_retry composer install --prefer-dist
-
-script:
-    - vendor/bin/phpunit --exclude-group=functional
diff --git a/CalculateRootJobStatusProcessor.php b/CalculateRootJobStatusProcessor.php
index 8ccac2c..96e7db2 100644
--- a/CalculateRootJobStatusProcessor.php
+++ b/CalculateRootJobStatusProcessor.php
@@ -2,17 +2,17 @@
 
 namespace Enqueue\JobQueue;
 
+use Enqueue\Client\CommandSubscriberInterface;
 use Enqueue\Client\ProducerInterface;
-use Enqueue\Client\TopicSubscriberInterface;
 use Enqueue\Consumption\Result;
 use Enqueue\JobQueue\Doctrine\JobStorage;
 use Enqueue\Util\JSON;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Message;
+use Interop\Queue\Processor;
 use Psr\Log\LoggerInterface;
 
-class CalculateRootJobStatusProcessor implements PsrProcessor, TopicSubscriberInterface
+class CalculateRootJobStatusProcessor implements Processor, CommandSubscriberInterface
 {
     /**
      * @var JobStorage
@@ -34,17 +34,11 @@ class CalculateRootJobStatusProcessor implements PsrProcessor, TopicSubscriberIn
      */
     private $logger;
 
-    /**
-     * @param JobStorage                    $jobStorage
-     * @param CalculateRootJobStatusService $calculateRootJobStatusCase
-     * @param ProducerInterface             $producer
-     * @param LoggerInterface               $logger
-     */
     public function __construct(
         JobStorage $jobStorage,
         CalculateRootJobStatusService $calculateRootJobStatusCase,
         ProducerInterface $producer,
-        LoggerInterface $logger
+        LoggerInterface $logger,
     ) {
         $this->jobStorage = $jobStorage;
         $this->calculateRootJobStatusService = $calculateRootJobStatusCase;
@@ -52,10 +46,7 @@ public function __construct(
         $this->logger = $logger;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function process(PsrMessage $message, PsrContext $context)
+    public function process(Message $message, Context $context)
     {
         $data = JSON::decode($message->getBody());
 
@@ -83,11 +74,8 @@ public function process(PsrMessage $message, PsrContext $context)
         return Result::ACK;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public static function getSubscribedTopics()
+    public static function getSubscribedCommand()
     {
-        return [Topics::CALCULATE_ROOT_JOB_STATUS];
+        return Commands::CALCULATE_ROOT_JOB_STATUS;
     }
 }
diff --git a/CalculateRootJobStatusService.php b/CalculateRootJobStatusService.php
index af4631e..41dd350 100644
--- a/CalculateRootJobStatusService.php
+++ b/CalculateRootJobStatusService.php
@@ -12,17 +12,12 @@ class CalculateRootJobStatusService
      */
     private $jobStorage;
 
-    /**
-     * @param JobStorage $jobStorage
-     */
     public function __construct(JobStorage $jobStorage)
     {
         $this->jobStorage = $jobStorage;
     }
 
     /**
-     * @param Job $job
-     *
      * @return bool true if root job was stopped
      */
     public function calculate(Job $job)
@@ -74,6 +69,7 @@ protected function calculateRootJobStatus(array $jobs)
         $success = 0;
 
         foreach ($jobs as $job) {
+            $this->jobStorage->refreshJobEntity($job);
             switch ($job->getStatus()) {
                 case Job::STATUS_NEW:
                     $new++;
@@ -91,11 +87,7 @@ protected function calculateRootJobStatus(array $jobs)
                     $success++;
                     break;
                 default:
-                    throw new \LogicException(sprintf(
-                        'Got unsupported job status: id: "%s" status: "%s"',
-                        $job->getId(),
-                        $job->getStatus()
-                    ));
+                    throw new \LogicException(sprintf('Got unsupported job status: id: "%s" status: "%s"', $job->getId(), $job->getStatus()));
             }
         }
 
diff --git a/Commands.php b/Commands.php
new file mode 100644
index 0000000..ae744c9
--- /dev/null
+++ b/Commands.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Enqueue\JobQueue;
+
+class Commands
+{
+    public const CALCULATE_ROOT_JOB_STATUS = 'enqueue.message_queue.job.calculate_root_job_status';
+}
diff --git a/DependentJobContext.php b/DependentJobContext.php
index 34deba6..04b898f 100644
--- a/DependentJobContext.php
+++ b/DependentJobContext.php
@@ -14,9 +14,6 @@ class DependentJobContext
      */
     private $dependentJobs;
 
-    /**
-     * @param Job $job
-     */
     public function __construct(Job $job)
     {
         $this->job = $job;
diff --git a/DependentJobProcessor.php b/DependentJobProcessor.php
index fb4ab1d..ac3055b 100644
--- a/DependentJobProcessor.php
+++ b/DependentJobProcessor.php
@@ -2,18 +2,18 @@
 
 namespace Enqueue\JobQueue;
 
-use Enqueue\Client\Message;
+use Enqueue\Client\Message as ClientMessage;
 use Enqueue\Client\ProducerInterface;
 use Enqueue\Client\TopicSubscriberInterface;
 use Enqueue\Consumption\Result;
 use Enqueue\JobQueue\Doctrine\JobStorage;
 use Enqueue\Util\JSON;
-use Interop\Queue\PsrContext;
-use Interop\Queue\PsrMessage;
-use Interop\Queue\PsrProcessor;
+use Interop\Queue\Context;
+use Interop\Queue\Message;
+use Interop\Queue\Processor;
 use Psr\Log\LoggerInterface;
 
-class DependentJobProcessor implements PsrProcessor, TopicSubscriberInterface
+class DependentJobProcessor implements Processor, TopicSubscriberInterface
 {
     /**
      * @var JobStorage
@@ -30,11 +30,6 @@ class DependentJobProcessor implements PsrProcessor, TopicSubscriberInterface
      */
     private $logger;
 
-    /**
-     * @param JobStorage        $jobStorage
-     * @param ProducerInterface $producer
-     * @param LoggerInterface   $logger
-     */
     public function __construct(JobStorage $jobStorage, ProducerInterface $producer, LoggerInterface $logger)
     {
         $this->jobStorage = $jobStorage;
@@ -42,10 +37,7 @@ public function __construct(JobStorage $jobStorage, ProducerInterface $producer,
         $this->logger = $logger;
     }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function process(PsrMessage $message, PsrContext $context)
+    public function process(Message $message, Context $context)
     {
         $data = JSON::decode($message->getBody());
 
@@ -98,7 +90,7 @@ public function process(PsrMessage $message, PsrContext $context)
         }
 
         foreach ($dependentJobs as $dependentJob) {
-            $message = new Message();
+            $message = new ClientMessage();
             $message->setBody($dependentJob['message']);
 
             if (isset($dependentJob['priority'])) {
@@ -111,11 +103,8 @@ public function process(PsrMessage $message, PsrContext $context)
         return Result::ACK;
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public static function getSubscribedTopics()
     {
-        return [Topics::ROOT_JOB_STOPPED];
+        return Topics::ROOT_JOB_STOPPED;
     }
 }
diff --git a/DependentJobService.php b/DependentJobService.php
index b6066d6..9ab500b 100644
--- a/DependentJobService.php
+++ b/DependentJobService.php
@@ -20,8 +20,6 @@ public function __construct(JobStorage $jobStorage)
     }
 
     /**
-     * @param Job $job
-     *
      * @return DependentJobContext
      */
     public function createDependentJobContext(Job $job)
@@ -29,16 +27,10 @@ public function createDependentJobContext(Job $job)
         return new DependentJobContext($job);
     }
 
-    /**
-     * @param DependentJobContext $context
-     */
     public function saveDependentJob(DependentJobContext $context)
     {
         if (!$context->getJob()->isRoot()) {
-            throw new \LogicException(sprintf(
-                'Only root jobs allowed but got child. jobId: "%s"',
-                $context->getJob()->getId()
-            ));
+            throw new \LogicException(sprintf('Only root jobs allowed but got child. jobId: "%s"', $context->getJob()->getId()));
         }
 
         $this->jobStorage->saveJob($context->getJob(), function (Job $job) use ($context) {
diff --git a/Doctrine/JobStorage.php b/Doctrine/JobStorage.php
index 32e20fe..4db5856 100644
--- a/Doctrine/JobStorage.php
+++ b/Doctrine/JobStorage.php
@@ -2,12 +2,12 @@
 
 namespace Enqueue\JobQueue\Doctrine;
 
-use Doctrine\Common\Persistence\ManagerRegistry;
 use Doctrine\DBAL\Connection;
 use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
 use Doctrine\DBAL\LockMode;
 use Doctrine\ORM\EntityManager;
 use Doctrine\ORM\EntityRepository;
+use Doctrine\Persistence\ManagerRegistry;
 use Enqueue\JobQueue\DuplicateJobException;
 use Enqueue\JobQueue\Job;
 
@@ -39,9 +39,8 @@ class JobStorage
     private $uniqueTableName;
 
     /**
-     * @param ManagerRegistry $doctrine
-     * @param string          $entityClass
-     * @param string          $uniqueTableName
+     * @param string $entityClass
+     * @param string $uniqueTableName
      */
     public function __construct(ManagerRegistry $doctrine, $entityClass, $uniqueTableName)
     {
@@ -59,13 +58,18 @@ public function findJobById($id)
     {
         $qb = $this->getEntityRepository()->createQueryBuilder('job');
 
-        return $qb
+        $job = $qb
             ->addSelect('rootJob')
             ->leftJoin('job.rootJob', 'rootJob')
             ->where('job = :id')
             ->setParameter('id', $id)
             ->getQuery()->getOneOrNullResult()
         ;
+        if ($job) {
+            $this->refreshJobEntity($job);
+        }
+
+        return $job;
     }
 
     /**
@@ -90,7 +94,6 @@ public function findRootJobByOwnerIdAndJobName($ownerId, $jobName)
 
     /**
      * @param string $name
-     * @param Job    $rootJob
      *
      * @return Job
      */
@@ -119,20 +122,13 @@ public function createJob()
     }
 
     /**
-     * @param Job           $job
-     * @param \Closure|null $lockCallback
-     *
      * @throws DuplicateJobException
      */
-    public function saveJob(Job $job, \Closure $lockCallback = null)
+    public function saveJob(Job $job, ?\Closure $lockCallback = null)
     {
         $class = $this->getEntityRepository()->getClassName();
         if (!$job instanceof $class) {
-            throw new \LogicException(sprintf(
-                'Got unexpected job instance: expected: "%s", actual" "%s"',
-                $class,
-                get_class($job)
-            ));
+            throw new \LogicException(sprintf('Got unexpected job instance: expected: "%s", actual" "%s"', $class, $job::class));
         }
 
         if ($lockCallback) {
@@ -175,11 +171,7 @@ public function saveJob(Job $job, \Closure $lockCallback = null)
                             ]);
                         }
                     } catch (UniqueConstraintViolationException $e) {
-                        throw new DuplicateJobException(sprintf(
-                            'Duplicate job. ownerId:"%s", name:"%s"',
-                            $job->getOwnerId(),
-                            $job->getName()
-                        ));
+                        throw new DuplicateJobException(sprintf('Duplicate job. ownerId:"%s", name:"%s"', $job->getOwnerId(), $job->getName()));
                     }
 
                     $this->getEntityManager()->persist($job);
@@ -192,6 +184,14 @@ public function saveJob(Job $job, \Closure $lockCallback = null)
         }
     }
 
+    /**
+     * @param Job $job
+     */
+    public function refreshJobEntity($job)
+    {
+        $this->getEntityManager()->refresh($job);
+    }
+
     /**
      * @return EntityRepository
      */
diff --git a/Doctrine/mapping/Job.orm.xml b/Doctrine/mapping/Job.orm.xml
index e6fcbdd..d6f4815 100644
--- a/Doctrine/mapping/Job.orm.xml
+++ b/Doctrine/mapping/Job.orm.xml
@@ -12,7 +12,7 @@
         <field name="createdAt" column="created_at" type="datetime" nullable="false" />
         <field name="startedAt" column="started_at" type="datetime" nullable="true" />
         <field name="stoppedAt" column="stopped_at" type="datetime" nullable="true" />
-        <field name="data" column="data" type="json_array" nullable="true" />
+        <field name="data" column="data" type="json" nullable="true" />
         <many-to-one field="rootJob" target-entity="Enqueue\JobQueue\Doctrine\Entity\Job" inversed-by="childJobs">
             <join-column name="root_job_id" referenced-column-name="id" on-delete="CASCADE" />
         </many-to-one>
diff --git a/Job.php b/Job.php
index 0468d7f..ddf53c2 100644
--- a/Job.php
+++ b/Job.php
@@ -4,11 +4,11 @@
 
 class Job
 {
-    const STATUS_NEW = 'enqueue.job_queue.status.new';
-    const STATUS_RUNNING = 'enqueue.job_queue.status.running';
-    const STATUS_SUCCESS = 'enqueue.job_queue.status.success';
-    const STATUS_FAILED = 'enqueue.job_queue.status.failed';
-    const STATUS_CANCELLED = 'enqueue.job_queue.status.cancelled';
+    public const STATUS_NEW = 'enqueue.job_queue.status.new';
+    public const STATUS_RUNNING = 'enqueue.job_queue.status.running';
+    public const STATUS_SUCCESS = 'enqueue.job_queue.status.success';
+    public const STATUS_FAILED = 'enqueue.job_queue.status.failed';
+    public const STATUS_CANCELLED = 'enqueue.job_queue.status.cancelled';
 
     /**
      * @var int
@@ -216,8 +216,6 @@ public function getRootJob()
      * Do not call from the outside.
      *
      * @internal
-     *
-     * @param Job $rootJob
      */
     public function setRootJob(self $rootJob)
     {
@@ -237,8 +235,6 @@ public function getCreatedAt()
      * Do not call from the outside.
      *
      * @internal
-     *
-     * @param \DateTime $createdAt
      */
     public function setCreatedAt(\DateTime $createdAt)
     {
@@ -258,8 +254,6 @@ public function getStartedAt()
      * Do not call from the outside.
      *
      * @internal
-     *
-     * @param \DateTime $startedAt
      */
     public function setStartedAt(\DateTime $startedAt)
     {
@@ -279,8 +273,6 @@ public function getStoppedAt()
      * Do not call from the outside.
      *
      * @internal
-     *
-     * @param \DateTime $stoppedAt
      */
     public function setStoppedAt(\DateTime $stoppedAt)
     {
@@ -324,9 +316,6 @@ public function getData()
         return $this->data;
     }
 
-    /**
-     * @param array $data
-     */
     public function setData(array $data)
     {
         $this->data = $data;
diff --git a/JobProcessor.php b/JobProcessor.php
index 7acd840..06698f4 100644
--- a/JobProcessor.php
+++ b/JobProcessor.php
@@ -17,10 +17,6 @@ class JobProcessor
      */
     private $producer;
 
-    /**
-     * @param JobStorage        $jobStorage
-     * @param ProducerInterface $producer
-     */
     public function __construct(JobStorage $jobStorage, ProducerInterface $producer)
     {
         $this->jobStorage = $jobStorage;
@@ -74,7 +70,6 @@ public function findOrCreateRootJob($ownerId, $jobName, $unique = false)
 
     /**
      * @param string $jobName
-     * @param Job    $rootJob
      *
      * @return Job
      */
@@ -104,9 +99,6 @@ public function findOrCreateChildJob($jobName, Job $rootJob)
         return $job;
     }
 
-    /**
-     * @param Job $job
-     */
     public function startChildJob(Job $job)
     {
         if ($job->isRoot()) {
@@ -116,11 +108,7 @@ public function startChildJob(Job $job)
         $job = $this->jobStorage->findJobById($job->getId());
 
         if (Job::STATUS_NEW !== $job->getStatus()) {
-            throw new \LogicException(sprintf(
-                'Can start only new jobs: id: "%s", status: "%s"',
-                $job->getId(),
-                $job->getStatus()
-            ));
+            throw new \LogicException(sprintf('Can start only new jobs: id: "%s", status: "%s"', $job->getId(), $job->getStatus()));
         }
 
         $job->setStatus(Job::STATUS_RUNNING);
@@ -131,9 +119,6 @@ public function startChildJob(Job $job)
         $this->sendCalculateRootJobStatusEvent($job);
     }
 
-    /**
-     * @param Job $job
-     */
     public function successChildJob(Job $job)
     {
         if ($job->isRoot()) {
@@ -143,11 +128,7 @@ public function successChildJob(Job $job)
         $job = $this->jobStorage->findJobById($job->getId());
 
         if (Job::STATUS_RUNNING !== $job->getStatus()) {
-            throw new \LogicException(sprintf(
-                'Can success only running jobs. id: "%s", status: "%s"',
-                $job->getId(),
-                $job->getStatus()
-            ));
+            throw new \LogicException(sprintf('Can success only running jobs. id: "%s", status: "%s"', $job->getId(), $job->getStatus()));
         }
 
         $job->setStatus(Job::STATUS_SUCCESS);
@@ -158,9 +139,6 @@ public function successChildJob(Job $job)
         $this->sendCalculateRootJobStatusEvent($job);
     }
 
-    /**
-     * @param Job $job
-     */
     public function failChildJob(Job $job)
     {
         if ($job->isRoot()) {
@@ -170,11 +148,7 @@ public function failChildJob(Job $job)
         $job = $this->jobStorage->findJobById($job->getId());
 
         if (Job::STATUS_RUNNING !== $job->getStatus()) {
-            throw new \LogicException(sprintf(
-                'Can fail only running jobs. id: "%s", status: "%s"',
-                $job->getId(),
-                $job->getStatus()
-            ));
+            throw new \LogicException(sprintf('Can fail only running jobs. id: "%s", status: "%s"', $job->getId(), $job->getStatus()));
         }
 
         $job->setStatus(Job::STATUS_FAILED);
@@ -185,9 +159,6 @@ public function failChildJob(Job $job)
         $this->sendCalculateRootJobStatusEvent($job);
     }
 
-    /**
-     * @param Job $job
-     */
     public function cancelChildJob(Job $job)
     {
         if ($job->isRoot()) {
@@ -197,11 +168,7 @@ public function cancelChildJob(Job $job)
         $job = $this->jobStorage->findJobById($job->getId());
 
         if (!in_array($job->getStatus(), [Job::STATUS_NEW, Job::STATUS_RUNNING], true)) {
-            throw new \LogicException(sprintf(
-                'Can cancel only new or running jobs. id: "%s", status: "%s"',
-                $job->getId(),
-                $job->getStatus()
-            ));
+            throw new \LogicException(sprintf('Can cancel only new or running jobs. id: "%s", status: "%s"', $job->getId(), $job->getStatus()));
         }
 
         $job->setStatus(Job::STATUS_CANCELLED);
@@ -217,7 +184,6 @@ public function cancelChildJob(Job $job)
     }
 
     /**
-     * @param Job  $job
      * @param bool $force
      */
     public function interruptRootJob(Job $job, $force = false)
@@ -245,8 +211,6 @@ public function interruptRootJob(Job $job, $force = false)
 
     /**
      * @see https://github.com/php-enqueue/enqueue-dev/pull/222#issuecomment-336102749 See for rationale
-     *
-     * @param Job $job
      */
     protected function saveJob(Job $job)
     {
@@ -255,12 +219,10 @@ protected function saveJob(Job $job)
 
     /**
      * @see https://github.com/php-enqueue/enqueue-dev/pull/222#issuecomment-336102749 See for rationale
-     *
-     * @param Job $job
      */
     protected function sendCalculateRootJobStatusEvent(Job $job)
     {
-        $this->producer->sendEvent(Topics::CALCULATE_ROOT_JOB_STATUS, [
+        $this->producer->sendCommand(Commands::CALCULATE_ROOT_JOB_STATUS, [
             'jobId' => $job->getId(),
         ]);
     }
diff --git a/JobRunner.php b/JobRunner.php
index 3f7c3f9..e21e524 100644
--- a/JobRunner.php
+++ b/JobRunner.php
@@ -14,24 +14,17 @@ class JobRunner
      */
     private $rootJob;
 
-    /**
-     * @param JobProcessor $jobProcessor
-     * @param Job          $rootJob
-     */
-    public function __construct(JobProcessor $jobProcessor, Job $rootJob = null)
+    public function __construct(JobProcessor $jobProcessor, ?Job $rootJob = null)
     {
         $this->jobProcessor = $jobProcessor;
         $this->rootJob = $rootJob;
     }
 
     /**
-     * @param string   $ownerId
-     * @param string   $name
-     * @param callable $runCallback
+     * @param string $ownerId
+     * @param string $name
      *
      * @throws \Throwable|\Exception if $runCallback triggers an exception
-     *
-     * @return mixed
      */
     public function runUnique($ownerId, $name, callable $runCallback)
     {
@@ -51,10 +44,12 @@ public function runUnique($ownerId, $name, callable $runCallback)
         try {
             $result = call_user_func($runCallback, $jobRunner, $childJob);
         } catch (\Throwable $e) {
-            $this->jobProcessor->failChildJob($childJob);
-            throw $e;
-        } catch (\Exception $e) { // needed to support PHP 5.6
-            $this->jobProcessor->failChildJob($childJob);
+            try {
+                $this->jobProcessor->failChildJob($childJob);
+            } catch (\Throwable $t) {
+                throw new OrphanJobException(sprintf('Job cleanup failed. ID: "%s" Name: "%s"', $childJob->getId(), $childJob->getName()), 0, $e);
+            }
+
             throw $e;
         }
 
@@ -68,10 +63,7 @@ public function runUnique($ownerId, $name, callable $runCallback)
     }
 
     /**
-     * @param string   $name
-     * @param callable $startCallback
-     *
-     * @return mixed
+     * @param string $name
      */
     public function createDelayed($name, callable $startCallback)
     {
@@ -83,10 +75,7 @@ public function createDelayed($name, callable $startCallback)
     }
 
     /**
-     * @param string   $jobId
-     * @param callable $runCallback
-     *
-     * @return mixed
+     * @param string $jobId
      */
     public function runDelayed($jobId, callable $runCallback)
     {
diff --git a/OrphanJobException.php b/OrphanJobException.php
new file mode 100644
index 0000000..37eac5c
--- /dev/null
+++ b/OrphanJobException.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Enqueue\JobQueue;
+
+class OrphanJobException extends \Exception
+{
+}
diff --git a/README.md b/README.md
index 5639a04..6fe6241 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,38 @@
+<h2 align="center">Supporting Enqueue</h2>
+
+Enqueue is an MIT-licensed open source project with its ongoing development made possible entirely by the support of community and our customers. If you'd like to join them, please consider:
+
+- [Become a sponsor](https://www.patreon.com/makasim)
+- [Become our client](http://forma-pro.com/)
+
+---
+
 # Job Queue.
 
 [![Gitter](https://badges.gitter.im/php-enqueue/Lobby.svg)](https://gitter.im/php-enqueue/Lobby)
-[![Build Status](https://travis-ci.org/php-enqueue/job-queue.png?branch=master)](https://travis-ci.org/php-enqueue/job-queue)
+[![Build Status](https://img.shields.io/github/actions/workflow/status/php-enqueue/job-queue/ci.yml?branch=master)](https://github.com/php-enqueue/job-queue/actions?query=workflow%3ACI)
 [![Total Downloads](https://poser.pugx.org/enqueue/job-queue/d/total.png)](https://packagist.org/packages/enqueue/job-queue)
 [![Latest Stable Version](https://poser.pugx.org/enqueue/job-queue/version.png)](https://packagist.org/packages/enqueue/job-queue)
- 
-There is job queue component build on top of a transport. 
-It provides some additional features like: unique job, sub jobs, dependent job and so. 
+
+There is job queue component build on top of a transport.
+It provides some additional features like: unique job, sub jobs, dependent job and so.
 Read more about it in documentation
 
 ## Resources
 
 * [Site](https://enqueue.forma-pro.com/)
-* [Documentation](https://github.com/php-enqueue/enqueue-dev/blob/master/docs/index.md)
+* [Documentation](https://php-enqueue.github.io/)
 * [Questions](https://gitter.im/php-enqueue/Lobby)
 * [Issue Tracker](https://github.com/php-enqueue/enqueue-dev/issues)
 
 ## Developed by Forma-Pro
 
-Forma-Pro is a full stack development company which interests also spread to open source development. 
-Being a team of strong professionals we have an aim an ability to help community by developing cutting edge solutions in the areas of e-commerce, docker & microservice oriented architecture where we have accumulated a huge many-years experience. 
+Forma-Pro is a full stack development company which interests also spread to open source development.
+Being a team of strong professionals we have an aim an ability to help community by developing cutting edge solutions in the areas of e-commerce, docker & microservice oriented architecture where we have accumulated a huge many-years experience.
 Our main specialization is Symfony framework based solution, but we are always looking to the technologies that allow us to do our job the best way. We are committed to creating solutions that revolutionize the way how things are developed in aspects of architecture & scalability.
 
 If you have any questions and inquires about our open source development, this product particularly or any other matter feel free to contact at opensource@forma-pro.com
 
 ## License
 
-It is released under the [MIT License](LICENSE).
\ No newline at end of file
+It is released under the [MIT License](LICENSE).
diff --git a/Schema.php b/Schema.php
deleted file mode 100644
index 71d6985..0000000
--- a/Schema.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-namespace Enqueue\JobQueue;
-
-use Doctrine\DBAL\Connection;
-use Doctrine\DBAL\Schema\Schema as BaseSchema;
-
-/**
- * @deprecated since 0.8, will be removed in 0.9
- */
-class Schema extends BaseSchema
-{
-    /**
-     * @var string
-     */
-    private $uniqueTableName;
-
-    /**
-     * @param Connection $connection
-     * @param string     $uniqueTableName
-     */
-    public function __construct(Connection $connection, $uniqueTableName)
-    {
-        $this->uniqueTableName = $uniqueTableName;
-
-        $schemaConfig = $connection->getSchemaManager()->createSchemaConfig();
-
-        parent::__construct([], [], $schemaConfig);
-
-        $this->addUniqueJobTable();
-    }
-
-    /**
-     * Merges ACL schema with the given schema.
-     *
-     * @param BaseSchema $schema
-     */
-    public function addToSchema(BaseSchema $schema)
-    {
-        foreach ($this->getTables() as $table) {
-            $schema->_addTable($table);
-        }
-
-        foreach ($this->getSequences() as $sequence) {
-            $schema->_addSequence($sequence);
-        }
-    }
-
-    private function addUniqueJobTable()
-    {
-        $table = $this->createTable($this->uniqueTableName);
-        $table->addColumn('name', 'string', ['length' => 255]);
-        $table->addUniqueIndex(['name']);
-    }
-}
diff --git a/Test/DbalPersistedConnection.php b/Test/DbalPersistedConnection.php
index 7c06f5a..470a651 100644
--- a/Test/DbalPersistedConnection.php
+++ b/Test/DbalPersistedConnection.php
@@ -22,9 +22,6 @@ class DbalPersistedConnection extends Connection
      */
     protected static $persistedTransactionNestingLevels;
 
-    /**
-     * {@inheritdoc}
-     */
     public function connect()
     {
         if ($this->isConnected()) {
@@ -33,7 +30,6 @@ public function connect()
 
         if ($this->hasPersistedConnection()) {
             $this->_conn = $this->getPersistedConnection();
-            $this->setConnected(true);
         } else {
             parent::connect();
             $this->persistConnection($this->_conn);
@@ -42,39 +38,25 @@ public function connect()
         return true;
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function beginTransaction()
     {
         $this->wrapTransactionNestingLevel('beginTransaction');
+
+        return true;
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function commit()
     {
         $this->wrapTransactionNestingLevel('commit');
+
+        return true;
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function rollBack()
     {
         $this->wrapTransactionNestingLevel('rollBack');
-    }
 
-    /**
-     * @param bool $connected
-     */
-    protected function setConnected($connected)
-    {
-        $isConnected = new \ReflectionProperty('Doctrine\DBAL\Connection', '_isConnected');
-        $isConnected->setAccessible(true);
-        $isConnected->setValue($this, $connected);
-        $isConnected->setAccessible(false);
+        return true;
     }
 
     /**
@@ -97,9 +79,6 @@ protected function persistTransactionNestingLevel($level)
         static::$persistedTransactionNestingLevels[$this->getConnectionId()] = $level;
     }
 
-    /**
-     * @param DriverConnection $connection
-     */
     protected function persistConnection(DriverConnection $connection)
     {
         static::$persistedConnections[$this->getConnectionId()] = $connection;
@@ -134,10 +113,15 @@ protected function getConnectionId()
      */
     private function setTransactionNestingLevel($level)
     {
-        $prop = new \ReflectionProperty('Doctrine\DBAL\Connection', '_transactionNestingLevel');
-        $prop->setAccessible(true);
-
-        return $prop->setValue($this, $level);
+        $rc = new \ReflectionClass(Connection::class);
+        $rp = $rc->hasProperty('transactionNestingLevel') ?
+            $rc->getProperty('transactionNestingLevel') :
+            $rc->getProperty('_transactionNestingLevel')
+        ;
+
+        $rp->setAccessible(true);
+        $rp->setValue($this, $level);
+        $rp->setAccessible(false);
     }
 
     /**
diff --git a/Test/JobRunner.php b/Test/JobRunner.php
index 62d9f9a..6194fbc 100644
--- a/Test/JobRunner.php
+++ b/Test/JobRunner.php
@@ -26,9 +26,6 @@ public function __construct()
     {
     }
 
-    /**
-     * {@inheritdoc}
-     */
     public function runUnique($ownerId, $jobName, \Closure $runCallback)
     {
         $this->runUniqueJobs[] = ['ownerId' => $ownerId, 'jobName' => $jobName, 'runCallback' => $runCallback];
@@ -36,11 +33,6 @@ public function runUnique($ownerId, $jobName, \Closure $runCallback)
         return call_user_func($runCallback, $this, new Job());
     }
 
-    /**
-     * {@inheritdoc}
-     *
-     * @return mixed
-     */
     public function createDelayed($jobName, \Closure $startCallback)
     {
         $this->createDelayedJobs[] = ['jobName' => $jobName, 'runCallback' => $startCallback];
@@ -48,11 +40,6 @@ public function createDelayed($jobName, \Closure $startCallback)
         return call_user_func($startCallback, $this, new Job());
     }
 
-    /**
-     * {@inheritdoc}
-     *
-     * @return mixed
-     */
     public function runDelayed($jobId, \Closure $runCallback)
     {
         $this->runDelayedJobs[] = ['jobId' => $jobId, 'runCallback' => $runCallback];
diff --git a/Tests/CalculateRootJobStatusProcessorTest.php b/Tests/CalculateRootJobStatusProcessorTest.php
index 2c86a01..8509f05 100644
--- a/Tests/CalculateRootJobStatusProcessorTest.php
+++ b/Tests/CalculateRootJobStatusProcessorTest.php
@@ -6,30 +6,22 @@
 use Enqueue\Consumption\Result;
 use Enqueue\JobQueue\CalculateRootJobStatusProcessor;
 use Enqueue\JobQueue\CalculateRootJobStatusService;
+use Enqueue\JobQueue\Commands;
 use Enqueue\JobQueue\Doctrine\JobStorage;
 use Enqueue\JobQueue\Job;
 use Enqueue\JobQueue\Topics;
 use Enqueue\Null\NullMessage;
-use Interop\Queue\PsrContext;
+use Interop\Queue\Context;
+use PHPUnit\Framework\MockObject\MockObject;
 use Psr\Log\LoggerInterface;
 
 class CalculateRootJobStatusProcessorTest extends \PHPUnit\Framework\TestCase
 {
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new CalculateRootJobStatusProcessor(
-            $this->createJobStorageMock(),
-            $this->createCalculateRootJobStatusCaseMock(),
-            $this->createProducerMock(),
-            $this->createLoggerMock()
-        );
-    }
-
     public function testShouldReturnSubscribedTopicNames()
     {
         $this->assertEquals(
-            [Topics::CALCULATE_ROOT_JOB_STATUS],
-            CalculateRootJobStatusProcessor::getSubscribedTopics()
+            Commands::CALCULATE_ROOT_JOB_STATUS,
+            CalculateRootJobStatusProcessor::getSubscribedCommand()
         );
     }
 
@@ -102,7 +94,7 @@ public function testShouldCallCalculateJobRootStatusAndACKMessage()
             ->expects($this->once())
             ->method('findJobById')
             ->with('12345')
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $logger = $this->createLoggerMock();
@@ -145,7 +137,7 @@ public function testShouldSendRootJobStoppedMessageIfJobHasStopped()
             ->expects($this->once())
             ->method('findJobById')
             ->with('12345')
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $logger = $this->createLoggerMock();
@@ -155,7 +147,7 @@ public function testShouldSendRootJobStoppedMessageIfJobHasStopped()
             ->expects($this->once())
             ->method('calculate')
             ->with($this->identicalTo($job))
-            ->will($this->returnValue(true))
+            ->willReturn(true)
         ;
 
         $producer = $this->createProducerMock();
@@ -175,7 +167,7 @@ public function testShouldSendRootJobStoppedMessageIfJobHasStopped()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
+     * @return MockObject|ProducerInterface
      */
     private function createProducerMock()
     {
@@ -183,15 +175,15 @@ private function createProducerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return MockObject|Context
      */
     private function createContextMock()
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(Context::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     * @return MockObject|LoggerInterface
      */
     private function createLoggerMock()
     {
@@ -199,7 +191,7 @@ private function createLoggerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|CalculateRootJobStatusService
+     * @return MockObject|CalculateRootJobStatusService
      */
     private function createCalculateRootJobStatusCaseMock()
     {
@@ -207,7 +199,7 @@ private function createCalculateRootJobStatusCaseMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|JobStorage
+     * @return MockObject|JobStorage
      */
     private function createJobStorageMock()
     {
diff --git a/Tests/CalculateRootJobStatusServiceTest.php b/Tests/CalculateRootJobStatusServiceTest.php
index 01f0d10..a0f1b4c 100644
--- a/Tests/CalculateRootJobStatusServiceTest.php
+++ b/Tests/CalculateRootJobStatusServiceTest.php
@@ -5,14 +5,10 @@
 use Enqueue\JobQueue\CalculateRootJobStatusService;
 use Enqueue\JobQueue\Doctrine\JobStorage;
 use Enqueue\JobQueue\Job;
+use PHPUnit\Framework\MockObject\MockObject;
 
 class CalculateRootJobStatusServiceTest extends \PHPUnit\Framework\TestCase
 {
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new CalculateRootJobStatusService($this->createJobStorageMock());
-    }
-
     public function stopStatusProvider()
     {
         return [
@@ -24,8 +20,6 @@ public function stopStatusProvider()
 
     /**
      * @dataProvider stopStatusProvider
-     *
-     * @param mixed $status
      */
     public function testShouldDoNothingIfRootJobHasStopState($status)
     {
@@ -60,9 +54,9 @@ public function testShouldCalculateRootJobStatus()
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $case = new CalculateRootJobStatusService($storage);
@@ -74,8 +68,6 @@ public function testShouldCalculateRootJobStatus()
 
     /**
      * @dataProvider stopStatusProvider
-     *
-     * @param mixed $stopStatus
      */
     public function testShouldCalculateRootJobStatusAndSetStoppedAtTimeIfGotStopStatus($stopStatus)
     {
@@ -92,16 +84,19 @@ public function testShouldCalculateRootJobStatusAndSetStoppedAtTimeIfGotStopStat
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $case = new CalculateRootJobStatusService($storage);
         $case->calculate($childJob);
 
         $this->assertEquals($stopStatus, $rootJob->getStatus());
-        $this->assertEquals(new \DateTime(), $rootJob->getStoppedAt(), '', 1);
+        $this->assertEquals(
+            (new \DateTime())->getTimestamp(),
+            $rootJob->getStoppedAt()->getTimestamp()
+        );
     }
 
     public function testShouldSetStoppedAtOnlyIfWasNotSet()
@@ -120,15 +115,18 @@ public function testShouldSetStoppedAtOnlyIfWasNotSet()
         $em
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $case = new CalculateRootJobStatusService($em);
         $case->calculate($childJob);
 
-        $this->assertEquals(new \DateTime('2012-12-12 12:12:12'), $rootJob->getStoppedAt());
+        $this->assertEquals(
+            (new \DateTime('2012-12-12 12:12:12'))->getTimestamp(),
+            $rootJob->getStoppedAt()->getTimestamp()
+        );
     }
 
     public function testShouldThrowIfInvalidStatus()
@@ -146,17 +144,15 @@ public function testShouldThrowIfInvalidStatus()
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $case = new CalculateRootJobStatusService($storage);
 
-        $this->setExpectedException(
-            \LogicException::class,
-            'Got unsupported job status: id: "12345" status: "invalid-status"'
-        );
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Got unsupported job status: id: "12345" status: "invalid-status"');
 
         $case->calculate($childJob);
     }
@@ -179,9 +175,9 @@ public function testShouldSetStatusNewIfAllChildAreNew()
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $case = new CalculateRootJobStatusService($storage);
@@ -212,9 +208,9 @@ public function testShouldSetStatusRunningIfAnyOneIsRunning()
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $case = new CalculateRootJobStatusService($storage);
@@ -245,9 +241,9 @@ public function testShouldSetStatusRunningIfThereIsNoRunningButNewAndAnyOfStopSt
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $case = new CalculateRootJobStatusService($storage);
@@ -278,9 +274,9 @@ public function testShouldSetStatusCancelledIfAllIsStopButOneIsCancelled()
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $case = new CalculateRootJobStatusService($storage);
@@ -311,9 +307,9 @@ public function testShouldSetStatusFailedIfThereIsAnyOneIsFailedButIsNotCancelle
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $case = new CalculateRootJobStatusService($storage);
@@ -344,9 +340,9 @@ public function testShouldSetStatusSuccessIfAllAreSuccess()
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $case = new CalculateRootJobStatusService($storage);
@@ -356,7 +352,7 @@ public function testShouldSetStatusSuccessIfAllAreSuccess()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|\Enqueue\JobQueue\Doctrine\JobStorage
+     * @return MockObject|JobStorage
      */
     private function createJobStorageMock()
     {
diff --git a/Tests/DependentJobContextTest.php b/Tests/DependentJobContextTest.php
index 32340d6..35942a9 100644
--- a/Tests/DependentJobContextTest.php
+++ b/Tests/DependentJobContextTest.php
@@ -7,11 +7,6 @@
 
 class DependentJobContextTest extends \PHPUnit\Framework\TestCase
 {
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new DependentJobContext(new Job());
-    }
-
     public function testShouldReturnJob()
     {
         $job = new Job();
diff --git a/Tests/DependentJobProcessorTest.php b/Tests/DependentJobProcessorTest.php
index a61918c..dcebf0d 100644
--- a/Tests/DependentJobProcessorTest.php
+++ b/Tests/DependentJobProcessorTest.php
@@ -10,7 +10,8 @@
 use Enqueue\JobQueue\Job;
 use Enqueue\JobQueue\Topics;
 use Enqueue\Null\NullMessage;
-use Interop\Queue\PsrContext;
+use Interop\Queue\Context;
+use PHPUnit\Framework\MockObject\MockObject;
 use Psr\Log\LoggerInterface;
 
 class DependentJobProcessorTest extends \PHPUnit\Framework\TestCase
@@ -18,7 +19,7 @@ class DependentJobProcessorTest extends \PHPUnit\Framework\TestCase
     public function testShouldReturnSubscribedTopicNames()
     {
         $this->assertEquals(
-            [Topics::ROOT_JOB_STOPPED],
+            Topics::ROOT_JOB_STOPPED,
             DependentJobProcessor::getSubscribedTopics()
         );
     }
@@ -84,7 +85,7 @@ public function testShouldLogCriticalAndRejectMessageIfJobIsNotRoot()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $producer = $this->createProducerMock();
@@ -115,7 +116,7 @@ public function testShouldDoNothingIfDependentJobsAreMissing()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $producer = $this->createProducerMock();
@@ -151,7 +152,7 @@ public function testShouldLogCriticalAndRejectMessageIfDependentJobTopicIsMissin
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $producer = $this->createProducerMock();
@@ -194,7 +195,7 @@ public function testShouldLogCriticalAndRejectMessageIfDependentJobMessageIsMiss
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $producer = $this->createProducerMock();
@@ -239,7 +240,7 @@ public function testShouldPublishDependentMessage()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $expectedMessage = null;
@@ -248,9 +249,9 @@ public function testShouldPublishDependentMessage()
             ->expects($this->once())
             ->method('sendEvent')
             ->with('topic-name', $this->isInstanceOf(Message::class))
-            ->will($this->returnCallback(function ($topic, Message $message) use (&$expectedMessage) {
+            ->willReturnCallback(function ($topic, Message $message) use (&$expectedMessage) {
                 $expectedMessage = $message;
-            }))
+            })
         ;
 
         $logger = $this->createLoggerMock();
@@ -287,7 +288,7 @@ public function testShouldPublishDependentMessageWithPriority()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $expectedMessage = null;
@@ -296,9 +297,9 @@ public function testShouldPublishDependentMessageWithPriority()
             ->expects($this->once())
             ->method('sendEvent')
             ->with('topic-name', $this->isInstanceOf(Message::class))
-            ->will($this->returnCallback(function ($topic, Message $message) use (&$expectedMessage) {
+            ->willReturnCallback(function ($topic, Message $message) use (&$expectedMessage) {
                 $expectedMessage = $message;
-            }))
+            })
         ;
 
         $logger = $this->createLoggerMock();
@@ -317,15 +318,15 @@ public function testShouldPublishDependentMessageWithPriority()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|PsrContext
+     * @return MockObject|Context
      */
     private function createContextMock()
     {
-        return $this->createMock(PsrContext::class);
+        return $this->createMock(Context::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|\Enqueue\JobQueue\Doctrine\JobStorage
+     * @return MockObject|JobStorage
      */
     private function createJobStorageMock()
     {
@@ -333,7 +334,7 @@ private function createJobStorageMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ProducerInterface
+     * @return MockObject|ProducerInterface
      */
     private function createProducerMock()
     {
@@ -341,7 +342,7 @@ private function createProducerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|LoggerInterface
+     * @return MockObject|LoggerInterface
      */
     private function createLoggerMock()
     {
diff --git a/Tests/DependentJobServiceTest.php b/Tests/DependentJobServiceTest.php
index 7469b9f..c2a1c7b 100644
--- a/Tests/DependentJobServiceTest.php
+++ b/Tests/DependentJobServiceTest.php
@@ -6,14 +6,10 @@
 use Enqueue\JobQueue\DependentJobService;
 use Enqueue\JobQueue\Doctrine\JobStorage;
 use Enqueue\JobQueue\Job;
+use PHPUnit\Framework\MockObject\MockObject;
 
 class DependentJobServiceTest extends \PHPUnit\Framework\TestCase
 {
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new DependentJobService($this->createJobStorageMock());
-    }
-
     public function testShouldThrowIfJobIsNotRootJob()
     {
         $job = new Job();
@@ -24,7 +20,8 @@ public function testShouldThrowIfJobIsNotRootJob()
 
         $service = new DependentJobService($this->createJobStorageMock());
 
-        $this->setExpectedException(\LogicException::class, 'Only root jobs allowed but got child. jobId: "12345"');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Only root jobs allowed but got child. jobId: "12345"');
 
         $service->saveDependentJob($context);
     }
@@ -38,11 +35,11 @@ public function testShouldSaveDependentJobs()
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
 
                 return true;
-            }))
+            })
         ;
 
         $context = new DependentJobContext($job);
@@ -66,7 +63,7 @@ public function testShouldSaveDependentJobs()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|\Enqueue\JobQueue\Doctrine\JobStorage
+     * @return MockObject|JobStorage
      */
     private function createJobStorageMock()
     {
diff --git a/Tests/Doctrine/JobStorageTest.php b/Tests/Doctrine/JobStorageTest.php
index 97fbc75..73f130d 100644
--- a/Tests/Doctrine/JobStorageTest.php
+++ b/Tests/Doctrine/JobStorageTest.php
@@ -2,30 +2,26 @@
 
 namespace Enqueue\JobQueue\Tests\Doctrine;
 
-use Doctrine\Common\Persistence\ManagerRegistry;
 use Doctrine\DBAL\Connection;
 use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
 use Doctrine\DBAL\LockMode;
 use Doctrine\ORM\EntityManager;
 use Doctrine\ORM\EntityRepository;
+use Doctrine\Persistence\ManagerRegistry;
 use Enqueue\JobQueue\Doctrine\JobStorage;
 use Enqueue\JobQueue\DuplicateJobException;
 use Enqueue\JobQueue\Job;
+use PHPUnit\Framework\MockObject\MockObject;
 
 class JobStorageTest extends \PHPUnit\Framework\TestCase
 {
-    public function testCouldBeConstructedWithRequiredArguments()
-    {
-        new JobStorage($this->createDoctrineMock(), 'entity-class', 'unique_table');
-    }
-
     public function testShouldCreateJobObject()
     {
         $repository = $this->createRepositoryMock();
         $repository
             ->expects($this->once())
             ->method('getClassName')
-            ->will($this->returnValue(Job::class))
+            ->willReturn(Job::class)
         ;
 
         $em = $this->createEntityManagerMock();
@@ -33,12 +29,12 @@ public function testShouldCreateJobObject()
             ->expects($this->once())
             ->method('getRepository')
             ->with('entity-class')
-            ->will($this->returnValue($repository))
+            ->willReturn($repository)
         ;
         $em
             ->expects($this->any())
             ->method('isOpen')
-            ->will($this->returnValue(true))
+            ->willReturn(true)
         ;
 
         $doctrine = $this->createDoctrineMock();
@@ -46,7 +42,7 @@ public function testShouldCreateJobObject()
             ->expects($this->once())
             ->method('getManagerForClass')
             ->with('entity-class')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
         $doctrine
             ->expects($this->never())
@@ -65,7 +61,7 @@ public function testShouldResetManagerAndCreateJobObject()
         $repository
             ->expects($this->once())
             ->method('getClassName')
-            ->will($this->returnValue(Job::class))
+            ->willReturn(Job::class)
         ;
 
         $em = $this->createEntityManagerMock();
@@ -73,12 +69,12 @@ public function testShouldResetManagerAndCreateJobObject()
             ->expects($this->once())
             ->method('getRepository')
             ->with('entity-class')
-            ->will($this->returnValue($repository))
+            ->willReturn($repository)
         ;
         $em
             ->expects($this->any())
             ->method('isOpen')
-            ->will($this->returnValue(false))
+            ->willReturn(false)
         ;
 
         $doctrine = $this->createDoctrineMock();
@@ -86,12 +82,12 @@ public function testShouldResetManagerAndCreateJobObject()
             ->expects($this->once())
             ->method('getManagerForClass')
             ->with('entity-class')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
         $doctrine
             ->expects($this->any())
             ->method('resetManager')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
 
         $storage = new JobStorage($doctrine, 'entity-class', 'unique_table');
@@ -106,7 +102,7 @@ public function testShouldThrowIfGotUnexpectedJobInstance()
         $repository
             ->expects($this->once())
             ->method('getClassName')
-            ->will($this->returnValue('expected\class\name'))
+            ->willReturn('expected\class\name')
         ;
 
         $em = $this->createEntityManagerMock();
@@ -114,12 +110,12 @@ public function testShouldThrowIfGotUnexpectedJobInstance()
             ->expects($this->once())
             ->method('getRepository')
             ->with('entity-class')
-            ->will($this->returnValue($repository))
+            ->willReturn($repository)
         ;
         $em
             ->expects($this->any())
             ->method('isOpen')
-            ->will($this->returnValue(true))
+            ->willReturn(true)
         ;
 
         $doctrine = $this->createDoctrineMock();
@@ -127,7 +123,7 @@ public function testShouldThrowIfGotUnexpectedJobInstance()
             ->expects($this->once())
             ->method('getManagerForClass')
             ->with('entity-class')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
         $doctrine
             ->expects($this->never())
@@ -136,11 +132,8 @@ public function testShouldThrowIfGotUnexpectedJobInstance()
 
         $storage = new JobStorage($doctrine, 'entity-class', 'unique_table');
 
-        $this->setExpectedException(
-            \LogicException::class,
-            'Got unexpected job instance: expected: "expected\class\name", '.
-            'actual" "Enqueue\JobQueue\Job"'
-        );
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Got unexpected job instance: expected: "expected\class\name", actual" "Enqueue\JobQueue\Job"');
 
         $storage->saveJob(new Job());
     }
@@ -156,7 +149,7 @@ public function testShouldSaveJobWithoutLockIfThereIsNoCallbackAndChildJob()
         $repository
             ->expects($this->once())
             ->method('getClassName')
-            ->will($this->returnValue(Job::class))
+            ->willReturn(Job::class)
         ;
 
         $em = $this->createEntityManagerMock();
@@ -164,7 +157,7 @@ public function testShouldSaveJobWithoutLockIfThereIsNoCallbackAndChildJob()
             ->expects($this->once())
             ->method('getRepository')
             ->with('entity-class')
-            ->will($this->returnValue($repository))
+            ->willReturn($repository)
         ;
         $em
             ->expects($this->once())
@@ -182,7 +175,7 @@ public function testShouldSaveJobWithoutLockIfThereIsNoCallbackAndChildJob()
         $em
             ->expects($this->any())
             ->method('isOpen')
-            ->will($this->returnValue(true))
+            ->willReturn(true)
         ;
 
         $doctrine = $this->createDoctrineMock();
@@ -190,7 +183,7 @@ public function testShouldSaveJobWithoutLockIfThereIsNoCallbackAndChildJob()
             ->expects($this->once())
             ->method('getManagerForClass')
             ->with('entity-class')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
         $doctrine
             ->expects($this->never())
@@ -210,7 +203,7 @@ public function testShouldSaveJobWithLockIfWithCallback()
         $repository
             ->expects($this->once())
             ->method('getClassName')
-            ->will($this->returnValue(Job::class))
+            ->willReturn(Job::class)
         ;
 
         $em = $this->createEntityManagerMock();
@@ -218,7 +211,7 @@ public function testShouldSaveJobWithLockIfWithCallback()
             ->expects($this->once())
             ->method('getRepository')
             ->with('entity-class')
-            ->will($this->returnValue($repository))
+            ->willReturn($repository)
         ;
         $em
             ->expects($this->never())
@@ -236,7 +229,7 @@ public function testShouldSaveJobWithLockIfWithCallback()
         $em
             ->expects($this->any())
             ->method('isOpen')
-            ->will($this->returnValue(true))
+            ->willReturn(true)
         ;
 
         $doctrine = $this->createDoctrineMock();
@@ -244,7 +237,7 @@ public function testShouldSaveJobWithLockIfWithCallback()
             ->expects($this->once())
             ->method('getManagerForClass')
             ->with('entity-class')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
         $doctrine
             ->expects($this->never())
@@ -267,16 +260,16 @@ public function testShouldCatchUniqueConstraintViolationExceptionAndThrowDuplica
         $repository
             ->expects($this->once())
             ->method('getClassName')
-            ->will($this->returnValue(Job::class))
+            ->willReturn(Job::class)
         ;
 
         $connection = $this->createConnectionMock();
         $connection
             ->expects($this->once())
             ->method('transactional')
-            ->will($this->returnCallback(function ($callback) use ($connection) {
+            ->willReturnCallback(function ($callback) use ($connection) {
                 $callback($connection);
-            }))
+            })
         ;
         $connection
             ->expects($this->once())
@@ -289,17 +282,17 @@ public function testShouldCatchUniqueConstraintViolationExceptionAndThrowDuplica
             ->expects($this->once())
             ->method('getRepository')
             ->with('entity-class')
-            ->will($this->returnValue($repository))
+            ->willReturn($repository)
         ;
         $em
             ->expects($this->once())
             ->method('getConnection')
-            ->will($this->returnValue($connection))
+            ->willReturn($connection)
         ;
         $em
             ->expects($this->any())
             ->method('isOpen')
-            ->will($this->returnValue(true))
+            ->willReturn(true)
         ;
 
         $doctrine = $this->createDoctrineMock();
@@ -307,7 +300,7 @@ public function testShouldCatchUniqueConstraintViolationExceptionAndThrowDuplica
             ->expects($this->once())
             ->method('getManagerForClass')
             ->with('entity-class')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
         $doctrine
             ->expects($this->never())
@@ -316,7 +309,8 @@ public function testShouldCatchUniqueConstraintViolationExceptionAndThrowDuplica
 
         $storage = new JobStorage($doctrine, 'entity-class', 'unique_table');
 
-        $this->setExpectedException(DuplicateJobException::class, 'Duplicate job. ownerId:"owner-id", name:"job-name"');
+        $this->expectException(DuplicateJobException::class);
+        $this->expectExceptionMessage('Duplicate job. ownerId:"owner-id", name:"job-name"');
 
         $storage->saveJob($job);
     }
@@ -329,7 +323,7 @@ public function testShouldThrowIfTryToSaveNewEntityWithLock()
         $repository
             ->expects($this->once())
             ->method('getClassName')
-            ->will($this->returnValue(Job::class))
+            ->willReturn(Job::class)
         ;
 
         $em = $this->createEntityManagerMock();
@@ -337,12 +331,12 @@ public function testShouldThrowIfTryToSaveNewEntityWithLock()
             ->expects($this->once())
             ->method('getRepository')
             ->with('entity-class')
-            ->will($this->returnValue($repository))
+            ->willReturn($repository)
         ;
         $em
             ->expects($this->any())
             ->method('isOpen')
-            ->will($this->returnValue(true))
+            ->willReturn(true)
         ;
 
         $doctrine = $this->createDoctrineMock();
@@ -350,7 +344,7 @@ public function testShouldThrowIfTryToSaveNewEntityWithLock()
             ->expects($this->once())
             ->method('getManagerForClass')
             ->with('entity-class')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
         $doctrine
             ->expects($this->never())
@@ -359,11 +353,8 @@ public function testShouldThrowIfTryToSaveNewEntityWithLock()
 
         $storage = new JobStorage($doctrine, 'entity-class', 'unique_table');
 
-        $this->setExpectedException(
-            \LogicException::class,
-            'Is not possible to create new job with lock, only update is allowed'
-        );
-
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Is not possible to create new job with lock, only update is allowed');
         $storage->saveJob($job, function () {
         });
     }
@@ -378,13 +369,13 @@ public function testShouldLockEntityAndPassNewInstanceIntoCallback()
         $repository
             ->expects($this->once())
             ->method('getClassName')
-            ->will($this->returnValue(Job::class))
+            ->willReturn(Job::class)
         ;
         $repository
             ->expects($this->once())
             ->method('find')
             ->with(12345, LockMode::PESSIMISTIC_WRITE)
-            ->will($this->returnValue($lockedJob))
+            ->willReturn($lockedJob)
         ;
 
         $em = $this->createEntityManagerMock();
@@ -392,19 +383,19 @@ public function testShouldLockEntityAndPassNewInstanceIntoCallback()
             ->expects($this->once())
             ->method('getRepository')
             ->with('entity-class')
-            ->will($this->returnValue($repository))
+            ->willReturn($repository)
         ;
         $em
             ->expects($this->once())
             ->method('transactional')
-            ->will($this->returnCallback(function ($callback) use ($em) {
+            ->willReturnCallback(function ($callback) use ($em) {
                 $callback($em);
-            }))
+            })
         ;
         $em
             ->expects($this->any())
             ->method('isOpen')
-            ->will($this->returnValue(true))
+            ->willReturn(true)
         ;
 
         $doctrine = $this->createDoctrineMock();
@@ -412,7 +403,7 @@ public function testShouldLockEntityAndPassNewInstanceIntoCallback()
             ->expects($this->once())
             ->method('getManagerForClass')
             ->with('entity-class')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
         $doctrine
             ->expects($this->never())
@@ -439,9 +430,9 @@ public function testShouldInsertIntoUniqueTableIfJobIsUniqueAndNew()
         $connection
             ->expects($this->once())
             ->method('transactional')
-            ->will($this->returnCallback(function ($callback) use ($connection) {
+            ->willReturnCallback(function ($callback) use ($connection) {
                 $callback($connection);
-            }))
+            })
         ;
         $connection
             ->expects($this->at(0))
@@ -458,7 +449,7 @@ public function testShouldInsertIntoUniqueTableIfJobIsUniqueAndNew()
         $repository
             ->expects($this->once())
             ->method('getClassName')
-            ->will($this->returnValue(Job::class))
+            ->willReturn(Job::class)
         ;
 
         $em = $this->createEntityManagerMock();
@@ -466,12 +457,12 @@ public function testShouldInsertIntoUniqueTableIfJobIsUniqueAndNew()
             ->expects($this->once())
             ->method('getRepository')
             ->with('entity-class')
-            ->will($this->returnValue($repository))
+            ->willReturn($repository)
         ;
         $em
             ->expects($this->once())
             ->method('getConnection')
-            ->will($this->returnValue($connection))
+            ->willReturn($connection)
         ;
         $em
             ->expects($this->once())
@@ -484,7 +475,7 @@ public function testShouldInsertIntoUniqueTableIfJobIsUniqueAndNew()
         $em
             ->expects($this->any())
             ->method('isOpen')
-            ->will($this->returnValue(true))
+            ->willReturn(true)
         ;
 
         $doctrine = $this->createDoctrineMock();
@@ -492,7 +483,7 @@ public function testShouldInsertIntoUniqueTableIfJobIsUniqueAndNew()
             ->expects($this->once())
             ->method('getManagerForClass')
             ->with('entity-class')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
         $doctrine
             ->expects($this->never())
@@ -528,12 +519,12 @@ public function testShouldDeleteRecordFromUniqueTableIfJobIsUniqueAndStoppedAtIs
         $repository
             ->expects($this->once())
             ->method('getClassName')
-            ->will($this->returnValue(Job::class))
+            ->willReturn(Job::class)
         ;
         $repository
             ->expects($this->once())
             ->method('find')
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $em = $this->createEntityManagerMock();
@@ -541,24 +532,24 @@ public function testShouldDeleteRecordFromUniqueTableIfJobIsUniqueAndStoppedAtIs
             ->expects($this->once())
             ->method('getRepository')
             ->with('entity-class')
-            ->will($this->returnValue($repository))
+            ->willReturn($repository)
         ;
         $em
             ->expects($this->once())
             ->method('transactional')
-            ->will($this->returnCallback(function ($callback) use ($em) {
+            ->willReturnCallback(function ($callback) use ($em) {
                 $callback($em);
-            }))
+            })
         ;
         $em
             ->expects($this->exactly(2))
             ->method('getConnection')
-            ->will($this->returnValue($connection))
+            ->willReturn($connection)
         ;
         $em
             ->expects($this->any())
             ->method('isOpen')
-            ->will($this->returnValue(true))
+            ->willReturn(true)
         ;
 
         $doctrine = $this->createDoctrineMock();
@@ -566,7 +557,7 @@ public function testShouldDeleteRecordFromUniqueTableIfJobIsUniqueAndStoppedAtIs
             ->expects($this->once())
             ->method('getManagerForClass')
             ->with('entity-class')
-            ->will($this->returnValue($em))
+            ->willReturn($em)
         ;
         $doctrine
             ->expects($this->never())
@@ -579,7 +570,7 @@ public function testShouldDeleteRecordFromUniqueTableIfJobIsUniqueAndStoppedAtIs
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|ManagerRegistry
+     * @return MockObject|ManagerRegistry
      */
     private function createDoctrineMock()
     {
@@ -587,7 +578,7 @@ private function createDoctrineMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Connection
+     * @return MockObject|Connection
      */
     private function createConnectionMock()
     {
@@ -595,7 +586,7 @@ private function createConnectionMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|EntityManager
+     * @return MockObject|EntityManager
      */
     private function createEntityManagerMock()
     {
@@ -603,7 +594,7 @@ private function createEntityManagerMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|EntityRepository
+     * @return MockObject|EntityRepository
      */
     private function createRepositoryMock()
     {
@@ -611,7 +602,7 @@ private function createRepositoryMock()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|UniqueConstraintViolationException
+     * @return MockObject|UniqueConstraintViolationException
      */
     private function createUniqueConstraintViolationExceptionMock()
     {
diff --git a/Tests/Functional/Entity/Job.php b/Tests/Functional/Entity/Job.php
index b6e0308..ad90e58 100644
--- a/Tests/Functional/Entity/Job.php
+++ b/Tests/Functional/Entity/Job.php
@@ -6,97 +6,47 @@
 use Doctrine\ORM\Mapping as ORM;
 use Enqueue\JobQueue\Job as BaseJob;
 
-/**
- * @ORM\Entity
- * @ORM\Table(name="enqueue_job_queue")
- */
+#[ORM\Entity]
+#[ORM\Table(name: 'enqueue_job_queue')]
 class Job extends BaseJob
 {
-    /**
-     * @var int
-     *
-     * @ORM\Column(name="id", type="integer")
-     * @ORM\Id
-     * @ORM\GeneratedValue(strategy="AUTO")
-     */
+    #[ORM\Column(name: 'id', type: 'integer')]
+    #[ORM\Id]
+    #[ORM\GeneratedValue(strategy: 'AUTO')]
     protected $id;
 
-    /**
-     * @var string
-     *
-     * @ORM\Column(name="owner_id", type="string", nullable=true)
-     */
+    #[ORM\Column(name: 'owner_id', type: 'string', nullable: true)]
     protected $ownerId;
 
-    /**
-     * @var string
-     *
-     * @ORM\Column(name="name", type="string", nullable=false)
-     */
+    #[ORM\Column(name: 'name', type: 'string', nullable: false)]
     protected $name;
 
-    /**
-     * @var string
-     *
-     * @ORM\Column(name="status", type="string", nullable=false)
-     */
+    #[ORM\Column(name: 'status', type: 'string', nullable: false)]
     protected $status;
 
-    /**
-     * @var bool
-     *
-     * @ORM\Column(name="interrupted", type="boolean")
-     */
+    #[ORM\Column(name: 'interrupted', type: 'boolean')]
     protected $interrupted;
 
-    /**
-     * @var bool;
-     *
-     * @ORM\Column(name="`unique`", type="boolean")
-     */
+    #[ORM\Column(name: '`unique`', type: 'boolean')]
     protected $unique;
 
-    /**
-     * @var Job
-     *
-     * @ORM\ManyToOne(targetEntity="Job", inversedBy="childJobs")
-     * @ORM\JoinColumn(name="root_job_id", referencedColumnName="id", onDelete="CASCADE")
-     */
+    #[ORM\ManyToOne(targetEntity: 'Job', inversedBy: 'childJobs')]
+    #[ORM\JoinColumn(name: 'root_job_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
     protected $rootJob;
 
-    /**
-     * @var Job[]
-     *
-     * @ORM\OneToMany(targetEntity="Job", mappedBy="rootJob")
-     */
+    #[ORM\OneToMany(mappedBy: 'rootJob', targetEntity: 'Job')]
     protected $childJobs;
 
-    /**
-     * @var \DateTime
-     *
-     * @ORM\Column(name="created_at", type="datetime", nullable=false)
-     */
+    #[ORM\Column(name: 'created_at', type: 'datetime', nullable: false)]
     protected $createdAt;
 
-    /**
-     * @var \DateTime
-     *
-     * @ORM\Column(name="started_at", type="datetime", nullable=true)
-     */
+    #[ORM\Column(name: 'started_at', type: 'datetime', nullable: true)]
     protected $startedAt;
 
-    /**
-     * @var \DateTime
-     *
-     * @ORM\Column(name="stopped_at", type="datetime", nullable=true)
-     */
+    #[ORM\Column(name: 'stopped_at', type: 'datetime', nullable: true)]
     protected $stoppedAt;
 
-    /**
-     * @var array
-     *
-     * @ORM\Column(name="data", type="json_array", nullable=true)
-     */
+    #[ORM\Column(name: 'data', type: 'json', nullable: true)]
     protected $data;
 
     public function __construct()
diff --git a/Tests/Functional/Entity/JobUnique.php b/Tests/Functional/Entity/JobUnique.php
index 4d8a337..6d10ea2 100644
--- a/Tests/Functional/Entity/JobUnique.php
+++ b/Tests/Functional/Entity/JobUnique.php
@@ -4,15 +4,11 @@
 
 use Doctrine\ORM\Mapping as ORM;
 
-/**
- * @ORM\Entity
- * @ORM\Table(name="enqueue_job_queue_unique")
- */
+#[ORM\Entity]
+#[ORM\Table(name: 'enqueue_job_queue_unique')]
 class JobUnique
 {
-    /**
-     * @ORM\Id
-     * @ORM\Column(name="name", type="string", nullable=false)
-     */
+    #[ORM\Id]
+    #[ORM\Column(name: 'name', type: 'string', nullable: false)]
     protected $name;
 }
diff --git a/Tests/Functional/Job/JobStorageTest.php b/Tests/Functional/Job/JobStorageTest.php
index 76cf28e..e884c31 100644
--- a/Tests/Functional/Job/JobStorageTest.php
+++ b/Tests/Functional/Job/JobStorageTest.php
@@ -128,7 +128,7 @@ public function testShouldThrowIfDuplicateJob()
         $job2->setStatus(Job::STATUS_NEW);
         $job2->setCreatedAt(new \DateTime());
 
-        $this->setExpectedException(DuplicateJobException::class);
+        $this->expectException(DuplicateJobException::class);
 
         $this->getJobStorage()->saveJob($job2);
     }
@@ -138,14 +138,14 @@ public function testShouldThrowIfDuplicateJob()
      */
     private function getEntityManager()
     {
-        return $this->container->get('doctrine.orm.default_entity_manager');
+        return static::$container->get('doctrine.orm.default_entity_manager');
     }
 
     /**
-     * @return \Enqueue\JobQueue\Doctrine\JobStorage
+     * @return JobStorage
      */
     private function getJobStorage()
     {
-        return new JobStorage($this->container->get('doctrine'), Job::class, 'enqueue_job_queue_unique');
+        return new JobStorage(static::$container->get('doctrine'), Job::class, 'enqueue_job_queue_unique');
     }
 }
diff --git a/Tests/Functional/WebTestCase.php b/Tests/Functional/WebTestCase.php
index da4913e..781bb5d 100644
--- a/Tests/Functional/WebTestCase.php
+++ b/Tests/Functional/WebTestCase.php
@@ -11,36 +11,36 @@ abstract class WebTestCase extends BaseWebTestCase
     /**
      * @var Client
      */
-    protected $client;
+    protected static $client;
 
     /**
      * @var ContainerInterface
      */
-    protected $container;
+    protected static $container;
 
-    protected function setUp()
+    protected function setUp(): void
     {
         parent::setUp();
 
         static::$class = null;
 
-        $this->client = static::createClient();
-        $this->container = static::$kernel->getContainer();
+        static::$client = static::createClient();
+
+        if (false == static::$container) {
+            static::$container = static::$kernel->getContainer();
+        }
 
         $this->startTransaction();
     }
 
-    protected function tearDown()
+    protected function tearDown(): void
     {
         $this->rollbackTransaction();
-
         parent::tearDown();
+        static::ensureKernelShutdown();
     }
 
-    /**
-     * @return string
-     */
-    public static function getKernelClass()
+    public static function getKernelClass(): string
     {
         require_once __DIR__.'/app/AppKernel.php';
 
@@ -50,7 +50,7 @@ public static function getKernelClass()
     protected function startTransaction()
     {
         /** @var $em \Doctrine\ORM\EntityManager */
-        foreach ($this->container->get('doctrine')->getManagers() as $em) {
+        foreach (static::$container->get('doctrine')->getManagers() as $em) {
             $em->clear();
             $em->getConnection()->beginTransaction();
         }
@@ -58,15 +58,15 @@ protected function startTransaction()
 
     protected function rollbackTransaction()
     {
-        //the error can be thrown during setUp
-        //It would be caught by phpunit and tearDown called.
-        //In this case we could not rollback since container may not exist.
-        if (false == $this->container) {
+        // the error can be thrown during setUp
+        // It would be caught by phpunit and tearDown called.
+        // In this case we could not rollback since container may not exist.
+        if (false == static::$container) {
             return;
         }
 
         /** @var $em \Doctrine\ORM\EntityManager */
-        foreach ($this->container->get('doctrine')->getManagers() as $em) {
+        foreach (static::$container->get('doctrine')->getManagers() as $em) {
             $connection = $em->getConnection();
 
             while ($connection->isTransactionActive()) {
diff --git a/Tests/Functional/app/AppKernel.php b/Tests/Functional/app/AppKernel.php
index 0d60c66..b51969f 100644
--- a/Tests/Functional/app/AppKernel.php
+++ b/Tests/Functional/app/AppKernel.php
@@ -1,65 +1,42 @@
 <?php
 
-Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists');
-
 use Symfony\Component\Config\Loader\LoaderInterface;
 use Symfony\Component\HttpKernel\Kernel;
 
 class AppKernel extends Kernel
 {
-    /**
-     * @return array
-     */
-    public function registerBundles()
+    public function registerBundles(): iterable
     {
         $bundles = [
-            new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
-            new \Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
+            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
+            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
         ];
 
         return $bundles;
     }
 
-    /**
-     * @return string
-     */
-    public function getCacheDir()
+    public function getCacheDir(): string
     {
         return sys_get_temp_dir().'/EnqueueJobQueue/cache';
     }
 
-    /**
-     * @return string
-     */
-    public function getLogDir()
+    public function getLogDir(): string
     {
         return sys_get_temp_dir().'/EnqueueJobQueue/cache/logs';
     }
 
-    /**
-     * @param \Symfony\Component\Config\Loader\LoaderInterface $loader
-     */
     public function registerContainerConfiguration(LoaderInterface $loader)
     {
-        $loader->load(__DIR__.'/config/config.yml');
-    }
-
-    protected function getKernelParameters()
-    {
-        $parameters = parent::getKernelParameters();
+        if (self::VERSION_ID < 60000) {
+            $loader->load(__DIR__.'/config/config-sf5.yml');
 
-        // it works in all Symfony version, 2.8, 3.x, 4.x
-        $parameters['db.driver'] = getenv('DOCTRINE_DRIVER');
-        $parameters['db.host'] = getenv('DOCTRINE_HOST');
-        $parameters['db.port'] = getenv('DOCTRINE_PORT');
-        $parameters['db.name'] = getenv('DOCTRINE_DB_NAME');
-        $parameters['db.user'] = getenv('DOCTRINE_USER');
-        $parameters['db.password'] = getenv('DOCTRINE_PASSWORD');
+            return;
+        }
 
-        return $parameters;
+        $loader->load(__DIR__.'/config/config.yml');
     }
 
-    protected function getContainerClass()
+    protected function getContainerClass(): string
     {
         return parent::getContainerClass().'JobQueue';
     }
diff --git a/Tests/Functional/app/config/config-sf5.yml b/Tests/Functional/app/config/config-sf5.yml
new file mode 100644
index 0000000..dd3467e
--- /dev/null
+++ b/Tests/Functional/app/config/config-sf5.yml
@@ -0,0 +1,33 @@
+parameters:
+    locale: 'en'
+    secret: 'ThisTokenIsNotSoSecretChangeIt'
+
+framework:
+    #esi:             ~
+    #translator:      { fallback: "%locale%" }
+    test: ~
+    assets: false
+    session:
+        # the only option incompatible with Symfony 6
+        storage_id: session.storage.mock_file
+    secret:          "%secret%"
+    router:          { resource: "%kernel.project_dir%/config/routing.yml" }
+    default_locale:  "%locale%"
+
+doctrine:
+    dbal:
+        url:   "%env(DOCTRINE_DSN)%"
+        driver: pdo_mysql
+        charset:  UTF8
+        wrapper_class: "Enqueue\\JobQueue\\Test\\DbalPersistedConnection"
+    orm:
+        auto_generate_proxy_classes: true
+        auto_mapping: true
+        mappings:
+            TestEntity:
+                mapping: true
+                type: annotation
+                dir: '%kernel.project_dir%/Tests/Functional/Entity'
+                alias: 'EnqueueJobQueue'
+                prefix: 'Enqueue\JobQueue\Tests\Functional\Entity'
+                is_bundle: false
diff --git a/Tests/Functional/app/config/config.yml b/Tests/Functional/app/config/config.yml
index 122fdef..0121acd 100644
--- a/Tests/Functional/app/config/config.yml
+++ b/Tests/Functional/app/config/config.yml
@@ -7,21 +7,16 @@ framework:
     #translator:      { fallback: "%locale%" }
     test: ~
     assets: false
-    templating: false
     session:
-        storage_id: session.storage.mock_file
+        storage_factory_id: session.storage.factory.mock_file
     secret:          "%secret%"
-    router:          { resource: "%kernel.root_dir%/config/routing.yml" }
+    router:          { resource: "%kernel.project_dir%/config/routing.yml" }
     default_locale:  "%locale%"
 
 doctrine:
     dbal:
-        driver:   "%db.driver%"
-        host:     "%db.host%"
-        port:     "%db.port%"
-        dbname:   "%db.name%"
-        user:     "%db.user%"
-        password: "%db.password%"
+        url:   "%env(DOCTRINE_DSN)%"
+        driver: pdo_mysql
         charset:  UTF8
         wrapper_class: "Enqueue\\JobQueue\\Test\\DbalPersistedConnection"
     orm:
@@ -30,8 +25,8 @@ doctrine:
         mappings:
             TestEntity:
                 mapping: true
-                type: annotation
-                dir: '%kernel.root_dir%/../Entity'
+                type: attribute
+                dir: '%kernel.project_dir%/Tests/Functional/Entity'
                 alias: 'EnqueueJobQueue'
                 prefix: 'Enqueue\JobQueue\Tests\Functional\Entity'
                 is_bundle: false
diff --git a/Tests/JobProcessorTest.php b/Tests/JobProcessorTest.php
index bb23868..9f1c7b2 100644
--- a/Tests/JobProcessorTest.php
+++ b/Tests/JobProcessorTest.php
@@ -2,26 +2,23 @@
 
 namespace Enqueue\JobQueue\Tests;
 
-use Enqueue\Client\Producer;
+use Enqueue\Client\ProducerInterface;
+use Enqueue\JobQueue\Commands;
 use Enqueue\JobQueue\Doctrine\JobStorage;
 use Enqueue\JobQueue\DuplicateJobException;
 use Enqueue\JobQueue\Job;
 use Enqueue\JobQueue\JobProcessor;
-use Enqueue\JobQueue\Topics;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 
 class JobProcessorTest extends TestCase
 {
-    public function testCouldBeCreatedWithRequiredArguments()
-    {
-        new JobProcessor($this->createJobStorage(), $this->createProducerMock());
-    }
-
     public function testCreateRootJobShouldThrowIfOwnerIdIsEmpty()
     {
         $processor = new JobProcessor($this->createJobStorage(), $this->createProducerMock());
 
-        $this->setExpectedException(\LogicException::class, 'OwnerId must not be empty');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('OwnerId must not be empty');
 
         $processor->findOrCreateRootJob(null, 'job-name', true);
     }
@@ -30,7 +27,8 @@ public function testCreateRootJobShouldThrowIfNameIsEmpty()
     {
         $processor = new JobProcessor($this->createJobStorage(), $this->createProducerMock());
 
-        $this->setExpectedException(\LogicException::class, 'Job name must not be empty');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Job name must not be empty');
 
         $processor->findOrCreateRootJob('owner-id', null, true);
     }
@@ -43,7 +41,7 @@ public function testShouldCreateRootJobAndReturnIt()
         $storage
             ->expects($this->once())
             ->method('createJob')
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
         $storage
             ->expects($this->once())
@@ -57,8 +55,14 @@ public function testShouldCreateRootJobAndReturnIt()
 
         $this->assertSame($job, $result);
         $this->assertEquals(Job::STATUS_NEW, $job->getStatus());
-        $this->assertEquals(new \DateTime(), $job->getCreatedAt(), '', 1);
-        $this->assertEquals(new \DateTime(), $job->getStartedAt(), '', 1);
+        $this->assertEquals(
+            (new \DateTime())->getTimestamp(),
+            $job->getCreatedAt()->getTimestamp()
+        );
+        $this->assertEquals(
+            (new \DateTime())->getTimestamp(),
+            $job->getStartedAt()->getTimestamp()
+        );
         $this->assertNull($job->getStoppedAt());
         $this->assertEquals('job-name', $job->getName());
         $this->assertEquals('owner-id', $job->getOwnerId());
@@ -72,7 +76,7 @@ public function testShouldCatchDuplicateJobAndTryToFindJobByOwnerId()
         $storage
             ->expects($this->once())
             ->method('createJob')
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
         $storage
             ->expects($this->once())
@@ -84,7 +88,7 @@ public function testShouldCatchDuplicateJobAndTryToFindJobByOwnerId()
             ->expects($this->once())
             ->method('findRootJobByOwnerIdAndJobName')
             ->with('owner-id', 'job-name')
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $processor = new JobProcessor($storage, $this->createProducerMock());
@@ -98,7 +102,8 @@ public function testCreateChildJobShouldThrowIfNameIsEmpty()
     {
         $processor = new JobProcessor($this->createJobStorage(), $this->createProducerMock());
 
-        $this->setExpectedException(\LogicException::class, 'Job name must not be empty');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Job name must not be empty');
 
         $processor->findOrCreateChildJob(null, new Job());
     }
@@ -121,13 +126,13 @@ public function testCreateChildJobShouldFindAndReturnAlreadyCreatedJob()
             ->expects($this->once())
             ->method('findChildJobByName')
             ->with('job-name', $this->identicalTo($job))
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
         $storage
             ->expects($this->once())
             ->method('findJobById')
             ->with(123)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $processor = new JobProcessor($storage, $this->createProducerMock());
@@ -146,7 +151,7 @@ public function testCreateChildJobShouldCreateAndSaveJobAndPublishRecalculateRoo
         $storage
             ->expects($this->once())
             ->method('createJob')
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
         $storage
             ->expects($this->once())
@@ -157,20 +162,20 @@ public function testCreateChildJobShouldCreateAndSaveJobAndPublishRecalculateRoo
             ->expects($this->once())
             ->method('findChildJobByName')
             ->with('job-name', $this->identicalTo($job))
-            ->will($this->returnValue(null))
+            ->willReturn(null)
         ;
         $storage
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $producer = $this->createProducerMock();
         $producer
             ->expects($this->once())
-            ->method('sendEvent')
-            ->with(Topics::CALCULATE_ROOT_JOB_STATUS, ['jobId' => 12345])
+            ->method('sendCommand')
+            ->with(Commands::CALCULATE_ROOT_JOB_STATUS, ['jobId' => 12345])
         ;
 
         $processor = new JobProcessor($storage, $producer);
@@ -179,7 +184,10 @@ public function testCreateChildJobShouldCreateAndSaveJobAndPublishRecalculateRoo
 
         $this->assertSame($job, $result);
         $this->assertEquals(Job::STATUS_NEW, $job->getStatus());
-        $this->assertEquals(new \DateTime(), $job->getCreatedAt(), '', 1);
+        $this->assertEquals(
+            (new \DateTime())->getTimestamp(),
+            $job->getCreatedAt()->getTimestamp()
+        );
         $this->assertNull($job->getStartedAt());
         $this->assertNull($job->getStoppedAt());
         $this->assertEquals('job-name', $job->getName());
@@ -193,7 +201,8 @@ public function testStartChildJobShouldThrowIfRootJob()
         $rootJob = new Job();
         $rootJob->setId(12345);
 
-        $this->setExpectedException(\LogicException::class, 'Can\'t start root jobs. id: "12345"');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Can\'t start root jobs. id: "12345"');
 
         $processor->startChildJob($rootJob);
     }
@@ -210,15 +219,13 @@ public function testStartChildJobShouldThrowIfJobHasNotNewStatus()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $processor = new JobProcessor($storage, $this->createProducerMock());
 
-        $this->setExpectedException(
-            \LogicException::class,
-            'Can start only new jobs: id: "12345", status: "enqueue.job_queue.status.cancelled"'
-        );
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Can start only new jobs: id: "12345", status: "enqueue.job_queue.status.cancelled"');
 
         $processor->startChildJob($job);
     }
@@ -240,20 +247,23 @@ public function testStartJobShouldUpdateJobWithRunningStatusAndStartAtTime()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $producer = $this->createProducerMock();
         $producer
             ->expects($this->once())
-            ->method('sendEvent')
+            ->method('sendCommand')
         ;
 
         $processor = new JobProcessor($storage, $producer);
         $processor->startChildJob($job);
 
         $this->assertEquals(Job::STATUS_RUNNING, $job->getStatus());
-        $this->assertEquals(new \DateTime(), $job->getStartedAt(), '', 1);
+        $this->assertEquals(
+            (new \DateTime())->getTimestamp(),
+            $job->getStartedAt()->getTimestamp()
+        );
     }
 
     public function testSuccessChildJobShouldThrowIfRootJob()
@@ -263,7 +273,8 @@ public function testSuccessChildJobShouldThrowIfRootJob()
         $rootJob = new Job();
         $rootJob->setId(12345);
 
-        $this->setExpectedException(\LogicException::class, 'Can\'t success root jobs. id: "12345"');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Can\'t success root jobs. id: "12345"');
 
         $processor->successChildJob($rootJob);
     }
@@ -280,15 +291,13 @@ public function testSuccessChildJobShouldThrowIfJobHasNotRunningStatus()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $processor = new JobProcessor($storage, $this->createProducerMock());
 
-        $this->setExpectedException(
-            \LogicException::class,
-            'Can success only running jobs. id: "12345", status: "enqueue.job_queue.status.cancelled"'
-        );
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Can success only running jobs. id: "12345", status: "enqueue.job_queue.status.cancelled"');
 
         $processor->successChildJob($job);
     }
@@ -310,20 +319,23 @@ public function testSuccessJobShouldUpdateJobWithSuccessStatusAndStopAtTime()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $producer = $this->createProducerMock();
         $producer
             ->expects($this->once())
-            ->method('sendEvent')
+            ->method('sendCommand')
         ;
 
         $processor = new JobProcessor($storage, $producer);
         $processor->successChildJob($job);
 
         $this->assertEquals(Job::STATUS_SUCCESS, $job->getStatus());
-        $this->assertEquals(new \DateTime(), $job->getStoppedAt(), '', 1);
+        $this->assertEquals(
+            (new \DateTime())->getTimestamp(),
+            $job->getStoppedAt()->getTimestamp()
+        );
     }
 
     public function testFailChildJobShouldThrowIfRootJob()
@@ -333,7 +345,8 @@ public function testFailChildJobShouldThrowIfRootJob()
         $rootJob = new Job();
         $rootJob->setId(12345);
 
-        $this->setExpectedException(\LogicException::class, 'Can\'t fail root jobs. id: "12345"');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Can\'t fail root jobs. id: "12345"');
 
         $processor->failChildJob($rootJob);
     }
@@ -350,15 +363,13 @@ public function testFailChildJobShouldThrowIfJobHasNotRunningStatus()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $processor = new JobProcessor($storage, $this->createProducerMock());
 
-        $this->setExpectedException(
-            \LogicException::class,
-            'Can fail only running jobs. id: "12345", status: "enqueue.job_queue.status.cancelled"'
-        );
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Can fail only running jobs. id: "12345", status: "enqueue.job_queue.status.cancelled"');
 
         $processor->failChildJob($job);
     }
@@ -380,20 +391,23 @@ public function testFailJobShouldUpdateJobWithFailStatusAndStopAtTime()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $producer = $this->createProducerMock();
         $producer
             ->expects($this->once())
-            ->method('sendEvent')
+            ->method('sendCommand')
         ;
 
         $processor = new JobProcessor($storage, $producer);
         $processor->failChildJob($job);
 
         $this->assertEquals(Job::STATUS_FAILED, $job->getStatus());
-        $this->assertEquals(new \DateTime(), $job->getStoppedAt(), '', 1);
+        $this->assertEquals(
+            (new \DateTime())->getTimestamp(),
+            $job->getStoppedAt()->getTimestamp()
+        );
     }
 
     public function testCancelChildJobShouldThrowIfRootJob()
@@ -403,7 +417,8 @@ public function testCancelChildJobShouldThrowIfRootJob()
         $rootJob = new Job();
         $rootJob->setId(12345);
 
-        $this->setExpectedException(\LogicException::class, 'Can\'t cancel root jobs. id: "12345"');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Can\'t cancel root jobs. id: "12345"');
 
         $processor->cancelChildJob($rootJob);
     }
@@ -420,15 +435,13 @@ public function testCancelChildJobShouldThrowIfJobHasNotNewOrRunningStatus()
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $processor = new JobProcessor($storage, $this->createProducerMock());
 
-        $this->setExpectedException(
-            \LogicException::class,
-            'Can cancel only new or running jobs. id: "12345", status: "enqueue.job_queue.status.cancelled"'
-        );
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Can cancel only new or running jobs. id: "12345", status: "enqueue.job_queue.status.cancelled"');
 
         $processor->cancelChildJob($job);
     }
@@ -450,21 +463,27 @@ public function testCancelJobShouldUpdateJobWithCancelStatusAndStoppedAtTimeAndS
             ->expects($this->once())
             ->method('findJobById')
             ->with(12345)
-            ->will($this->returnValue($job))
+            ->willReturn($job)
         ;
 
         $producer = $this->createProducerMock();
         $producer
             ->expects($this->once())
-            ->method('sendEvent')
+            ->method('sendCommand')
         ;
 
         $processor = new JobProcessor($storage, $producer);
         $processor->cancelChildJob($job);
 
         $this->assertEquals(Job::STATUS_CANCELLED, $job->getStatus());
-        $this->assertEquals(new \DateTime(), $job->getStoppedAt(), '', 1);
-        $this->assertEquals(new \DateTime(), $job->getStartedAt(), '', 1);
+        $this->assertEquals(
+            (new \DateTime())->getTimestamp(),
+            $job->getStoppedAt()->getTimestamp()
+        );
+        $this->assertEquals(
+            (new \DateTime())->getTimestamp(),
+            $job->getStartedAt()->getTimestamp()
+        );
     }
 
     public function testInterruptRootJobShouldThrowIfNotRootJob()
@@ -475,7 +494,8 @@ public function testInterruptRootJobShouldThrowIfNotRootJob()
 
         $processor = new JobProcessor($this->createJobStorage(), $this->createProducerMock());
 
-        $this->setExpectedException(\LogicException::class, 'Can interrupt only root jobs. id: "123"');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Can interrupt only root jobs. id: "123"');
 
         $processor->interruptRootJob($notRootJob);
     }
@@ -505,9 +525,9 @@ public function testInterruptRootJobShouldUpdateJobAndSetInterruptedTrue()
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $processor = new JobProcessor($storage, $this->createProducerMock());
@@ -526,31 +546,34 @@ public function testInterruptRootJobShouldUpdateJobAndSetInterruptedTrueAndStopp
         $storage
             ->expects($this->once())
             ->method('saveJob')
-            ->will($this->returnCallback(function (Job $job, $callback) {
+            ->willReturnCallback(function (Job $job, $callback) {
                 $callback($job);
-            }))
+            })
         ;
 
         $processor = new JobProcessor($storage, $this->createProducerMock());
         $processor->interruptRootJob($rootJob, true);
 
         $this->assertTrue($rootJob->isInterrupted());
-        $this->assertEquals(new \DateTime(), $rootJob->getStoppedAt(), '', 1);
+        $this->assertEquals(
+            (new \DateTime())->getTimestamp(),
+            $rootJob->getStoppedAt()->getTimestamp()
+        );
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|JobStorage
+     * @return MockObject
      */
-    private function createJobStorage()
+    private function createJobStorage(): JobStorage
     {
         return $this->createMock(JobStorage::class);
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|Producer
+     * @return MockObject
      */
-    private function createProducerMock()
+    private function createProducerMock(): ProducerInterface
     {
-        return $this->createMock(Producer::class);
+        return $this->createMock(ProducerInterface::class);
     }
 }
diff --git a/Tests/JobRunnerTest.php b/Tests/JobRunnerTest.php
index 6087424..e43440f 100644
--- a/Tests/JobRunnerTest.php
+++ b/Tests/JobRunnerTest.php
@@ -5,6 +5,8 @@
 use Enqueue\JobQueue\Job;
 use Enqueue\JobQueue\JobProcessor;
 use Enqueue\JobQueue\JobRunner;
+use Enqueue\JobQueue\OrphanJobException;
+use PHPUnit\Framework\MockObject\MockObject;
 
 class JobRunnerTest extends \PHPUnit\Framework\TestCase
 {
@@ -18,13 +20,13 @@ public function testRunUniqueShouldCreateRootAndChildJobAndCallCallback()
             ->expects($this->once())
             ->method('findOrCreateRootJob')
             ->with('owner-id', 'job-name', true)
-            ->will($this->returnValue($root))
+            ->willReturn($root)
         ;
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateChildJob')
             ->with('job-name')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
 
         $expChild = null;
@@ -56,12 +58,12 @@ public function testRunUniqueShouldStartChildJobIfNotStarted()
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateRootJob')
-            ->will($this->returnValue($root))
+            ->willReturn($root)
         ;
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateChildJob')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
         $jobProcessor
             ->expects($this->once())
@@ -84,12 +86,12 @@ public function testRunUniqueShouldNotStartChildJobIfAlreadyStarted()
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateRootJob')
-            ->will($this->returnValue($root))
+            ->willReturn($root)
         ;
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateChildJob')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
         $jobProcessor
             ->expects($this->never())
@@ -110,12 +112,12 @@ public function testRunUniqueShouldSuccessJobIfCallbackReturnValueIsTrue()
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateRootJob')
-            ->will($this->returnValue($root))
+            ->willReturn($root)
         ;
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateChildJob')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
         $jobProcessor
             ->expects($this->once())
@@ -141,12 +143,12 @@ public function testRunUniqueShouldFailJobIfCallbackReturnValueIsFalse()
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateRootJob')
-            ->will($this->returnValue($root))
+            ->willReturn($root)
         ;
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateChildJob')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
         $jobProcessor
             ->expects($this->never())
@@ -172,12 +174,12 @@ public function testRunUniqueShouldFailJobIfCallbackThrowsException()
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateRootJob')
-            ->will($this->returnValue($root))
+            ->willReturn($root)
         ;
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateChildJob')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
         $jobProcessor
             ->expects($this->never())
@@ -195,6 +197,39 @@ public function testRunUniqueShouldFailJobIfCallbackThrowsException()
         });
     }
 
+    public function testRunUniqueShouldThrowOrphanJobExceptionIfChildCleanupFails()
+    {
+        $root = new Job();
+        $child = new Job();
+
+        $jobProcessor = $this->createJobProcessorMock();
+        $jobProcessor
+            ->expects($this->once())
+            ->method('findOrCreateRootJob')
+            ->willReturn($root)
+        ;
+        $jobProcessor
+            ->expects($this->once())
+            ->method('findOrCreateChildJob')
+            ->willReturn($child)
+        ;
+        $jobProcessor
+            ->expects($this->never())
+            ->method('successChildJob')
+        ;
+        $jobProcessor
+            ->expects($this->once())
+            ->method('failChildJob')
+            ->willThrowException(new \Exception())
+        ;
+
+        $jobRunner = new JobRunner($jobProcessor);
+        $this->expectException(OrphanJobException::class);
+        $jobRunner->runUnique('owner-id', 'job-name', function () {
+            throw new \Exception();
+        });
+    }
+
     public function testRunUniqueShouldNotSuccessJobIfJobIsAlreadyStopped()
     {
         $root = new Job();
@@ -205,12 +240,12 @@ public function testRunUniqueShouldNotSuccessJobIfJobIsAlreadyStopped()
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateRootJob')
-            ->will($this->returnValue($root))
+            ->willReturn($root)
         ;
         $jobProcessor
             ->expects($this->once())
             ->method('findOrCreateChildJob')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
         $jobProcessor
             ->expects($this->never())
@@ -237,7 +272,7 @@ public function testCreateDelayedShouldCreateChildJobAndCallCallback()
             ->expects($this->once())
             ->method('findOrCreateChildJob')
             ->with('job-name', $this->identicalTo($root))
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
 
         $expRunner = null;
@@ -262,12 +297,13 @@ public function testRunDelayedShouldThrowExceptionIfJobWasNotFoundById()
             ->expects($this->once())
             ->method('findJobById')
             ->with('job-id')
-            ->will($this->returnValue(null))
+            ->willReturn(null)
         ;
 
         $jobRunner = new JobRunner($jobProcessor);
 
-        $this->setExpectedException(\LogicException::class, 'Job was not found. id: "job-id"');
+        $this->expectException(\LogicException::class);
+        $this->expectExceptionMessage('Job was not found. id: "job-id"');
 
         $jobRunner->runDelayed('job-id', function () {
         });
@@ -284,7 +320,7 @@ public function testRunDelayedShouldFindJobAndCallCallback()
             ->expects($this->once())
             ->method('findJobById')
             ->with('job-id')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
 
         $expRunner = null;
@@ -314,7 +350,7 @@ public function testRunDelayedShouldCancelJobIfRootJobIsInterrupted()
             ->expects($this->once())
             ->method('findJobById')
             ->with('job-id')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
         $jobProcessor
             ->expects($this->once())
@@ -339,7 +375,7 @@ public function testRunDelayedShouldSuccessJobIfCallbackReturnValueIsTrue()
             ->expects($this->once())
             ->method('findJobById')
             ->with('job-id')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
         $jobProcessor
             ->expects($this->once())
@@ -368,7 +404,7 @@ public function testRunDelayedShouldFailJobIfCallbackReturnValueIsFalse()
             ->expects($this->once())
             ->method('findJobById')
             ->with('job-id')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
         $jobProcessor
             ->expects($this->never())
@@ -398,7 +434,7 @@ public function testRunDelayedShouldNotSuccessJobIfAlreadyStopped()
             ->expects($this->once())
             ->method('findJobById')
             ->with('job-id')
-            ->will($this->returnValue($child))
+            ->willReturn($child)
         ;
         $jobProcessor
             ->expects($this->never())
@@ -416,7 +452,7 @@ public function testRunDelayedShouldNotSuccessJobIfAlreadyStopped()
     }
 
     /**
-     * @return \PHPUnit_Framework_MockObject_MockObject|JobProcessor
+     * @return MockObject|JobProcessor
      */
     private function createJobProcessorMock()
     {
diff --git a/Topics.php b/Topics.php
index 664fa0b..891ea26 100644
--- a/Topics.php
+++ b/Topics.php
@@ -4,6 +4,5 @@
 
 class Topics
 {
-    const CALCULATE_ROOT_JOB_STATUS = 'enqueue.message_queue.job.calculate_root_job_status';
-    const ROOT_JOB_STOPPED = 'enqueue.message_queue.job.root_job_stopped';
+    public const ROOT_JOB_STOPPED = 'enqueue.message_queue.job.root_job_stopped';
 }
diff --git a/composer.json b/composer.json
index 2361a30..6616069 100644
--- a/composer.json
+++ b/composer.json
@@ -6,18 +6,21 @@
     "homepage": "https://enqueue.forma-pro.com/",
     "license": "MIT",
     "require": {
-        "php": ">=5.6",
-        "enqueue/enqueue": "^0.8@dev",
-        "enqueue/null": "^0.8@dev",
-        "doctrine/orm": "~2.4"
+        "php": "^8.1",
+        "enqueue/enqueue": "^0.10",
+        "enqueue/null": "^0.10",
+        "queue-interop/queue-interop": "^0.8",
+        "doctrine/orm": "^2.12",
+        "doctrine/dbal": "^2.12 | ^3.0"
     },
     "require-dev": {
-        "phpunit/phpunit": "~5.5",
-        "enqueue/test": "^0.8@dev",
-        "doctrine/doctrine-bundle": "~1.2",
-        "symfony/browser-kit": "^2.8|^3|^4",
-        "symfony/expression-language": "^2.8|^3|^4",
-        "symfony/framework-bundle": "^2.8|^3|^4"
+        "phpunit/phpunit": "^9.5",
+        "enqueue/test": "0.10.x-dev",
+        "doctrine/doctrine-bundle": "^2.3.2",
+        "symfony/browser-kit": "^5.4|^6.0",
+        "symfony/expression-language": "^5.4|^6.0",
+        "symfony/framework-bundle": "^5.4|^6.0",
+        "symfony/yaml": "^5.4|^6.0"
     },
     "support": {
         "email": "opensource@forma-pro.com",
@@ -35,7 +38,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-master": "0.8.x-dev"
+            "dev-master": "0.10.x-dev"
         }
     }
 }
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 29dc334..3665922 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,16 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<phpunit backupGlobals="false"
-         backupStaticAttributes="false"
-         colors="true"
-         convertErrorsToExceptions="true"
-         convertNoticesToExceptions="true"
-         convertWarningsToExceptions="true"
-         processIsolation="false"
-         stopOnFailure="false"
-         syntaxCheck="false"
-         bootstrap="./vendor/autoload.php"
-        >
+<phpunit
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
+    colors="true"
+    bootstrap="./vendor/autoload.php"
+>
 
     <testsuites>
         <testsuite name="Job Queue Test Suite">
