diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml
new file mode 100644
index 0000000..6775731
--- /dev/null
+++ b/.github/workflows/coding-standards.yml
@@ -0,0 +1,26 @@
+name: "Check Coding Standards"
+
+on:
+  pull_request:
+
+jobs:
+  coding-standards:
+    name: "Check Coding Standards"
+
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Setup PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: "7.4"
+          tools: composer:v2, cs2pr
+
+      - name: Install dependencies
+        run: composer require squizlabs/php_codesniffer --no-interaction --no-progress
+
+      - name: "Run phpcs"
+        run: vendor/bin/phpcs -q --standard=PSR2 classes/ tests/ --report=checkstyle | cs2pr
diff --git a/.github/workflows/phpmd.yml b/.github/workflows/phpmd.yml
new file mode 100644
index 0000000..4854690
--- /dev/null
+++ b/.github/workflows/phpmd.yml
@@ -0,0 +1,26 @@
+name: "Check phpmd"
+
+on:
+  pull_request:
+
+jobs:
+  coding-standards:
+    name: "Check phpmd"
+
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Setup PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: "7.4"
+          tools: composer:v2
+
+      - name: Install dependencies
+        run: composer require phpmd/phpmd --no-interaction --no-progress
+
+      - name: Run phpmd
+        run: vendor/bin/phpmd classes/ text cleancode,codesize,controversial,design,naming,unusedcode
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..62c6a59
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,609 @@
+on:
+  pull_request:
+  schedule:
+    - cron:  "0 0 * * *"
+
+concurrency:
+  group: ${{ github.head_ref || 'cron' }}
+  cancel-in-progress: true
+
+jobs:
+  tests:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        php-version:
+          - '8.4'
+          - '8.3'
+          - '8.2'
+          - '8.1'
+          - '8.0'
+          - '7.4'
+          - '7.3'
+          - '7.2'
+          - '7.1'
+          - '7.0'
+        phpunit-version:
+          - '12.1.0'
+          - '12.0.0'
+          - '11.5.0'
+          - '11.4.0'
+          - '11.3.0'
+          - '11.2.0'
+          - '11.1.0'
+          - '11.0.0'
+          - '10.5.0'
+          - '10.4.0'
+          - '10.3.0'
+          - '10.2.0'
+          - '10.1.0'
+          - '10.0.0'
+          - '9.6.0'
+          - '9.5.0'
+          - '9.4.0'
+          - '9.3.0'
+          - '9.2.0'
+          - '9.1.0'
+          - '9.0.0'
+          - '8.4.0'
+          - '8.3.0'
+          - '8.2.0'
+          - '8.1.0'
+          - '8.0.0'
+          - '7.5.0'
+          - '7.4.0'
+          - '7.3.0'
+          - '7.2.0'
+          - '7.1.0'
+          - '7.0.0'
+          - '6.5.0'
+          - '6.4.0'
+          - '6.3.0'
+          - '6.2.0'
+          - '6.1.0'
+          - '6.0.0'
+
+        exclude:
+          # PHP 8.4 Exclusions
+          - php-version: '8.4'
+            phpunit-version: '9.4.0'
+          - php-version: '8.4'
+            phpunit-version: '9.3.0'
+          - php-version: '8.4'
+            phpunit-version: '9.2.0'
+          - php-version: '8.4'
+            phpunit-version: '9.1.0'
+          - php-version: '8.4'
+            phpunit-version: '9.0.0'
+          - php-version: '8.4'
+            phpunit-version: '8.4.0'
+          - php-version: '8.4'
+            phpunit-version: '8.3.0'
+          - php-version: '8.4'
+            phpunit-version: '8.2.0'
+          - php-version: '8.4'
+            phpunit-version: '8.1.0'
+          - php-version: '8.4'
+            phpunit-version: '8.0.0'
+          - php-version: '8.4'
+            phpunit-version: '7.5.0'
+          - php-version: '8.4'
+            phpunit-version: '7.4.0'
+          - php-version: '8.4'
+            phpunit-version: '7.3.0'
+          - php-version: '8.4'
+            phpunit-version: '7.2.0'
+          - php-version: '8.4'
+            phpunit-version: '7.1.0'
+          - php-version: '8.4'
+            phpunit-version: '7.0.0'
+          - php-version: '8.4'
+            phpunit-version: '6.5.0'
+          - php-version: '8.4'
+            phpunit-version: '6.4.0'
+          - php-version: '8.4'
+            phpunit-version: '6.3.0'
+          - php-version: '8.4'
+            phpunit-version: '6.2.0'
+          - php-version: '8.4'
+            phpunit-version: '6.1.0'
+          - php-version: '8.4'
+            phpunit-version: '6.0.0'
+        
+          # PHP 8.3 Exclusions
+          - php-version: '8.3'
+            phpunit-version: '9.4.0'
+          - php-version: '8.3'
+            phpunit-version: '9.3.0'
+          - php-version: '8.3'
+            phpunit-version: '9.2.0'
+          - php-version: '8.3'
+            phpunit-version: '9.1.0'
+          - php-version: '8.3'
+            phpunit-version: '9.0.0'
+          - php-version: '8.3'
+            phpunit-version: '8.4.0'
+          - php-version: '8.3'
+            phpunit-version: '8.3.0'
+          - php-version: '8.3'
+            phpunit-version: '8.2.0'
+          - php-version: '8.3'
+            phpunit-version: '8.1.0'
+          - php-version: '8.3'
+            phpunit-version: '8.0.0'
+          - php-version: '8.3'
+            phpunit-version: '7.5.0'
+          - php-version: '8.3'
+            phpunit-version: '7.4.0'
+          - php-version: '8.3'
+            phpunit-version: '7.3.0'
+          - php-version: '8.3'
+            phpunit-version: '7.2.0'
+          - php-version: '8.3'
+            phpunit-version: '7.1.0'
+          - php-version: '8.3'
+            phpunit-version: '7.0.0'
+          - php-version: '8.3'
+            phpunit-version: '6.5.0'
+          - php-version: '8.3'
+            phpunit-version: '6.4.0'
+          - php-version: '8.3'
+            phpunit-version: '6.3.0'
+          - php-version: '8.3'
+            phpunit-version: '6.2.0'
+          - php-version: '8.3'
+            phpunit-version: '6.1.0'
+          - php-version: '8.3'
+            phpunit-version: '6.0.0'
+
+          # PHP 8.2 Exclusions
+          - php-version: '8.2'
+            phpunit-version: '12.1.0'
+          - php-version: '8.2'
+            phpunit-version: '12.0.0'
+          - php-version: '8.2'
+            phpunit-version: '9.4.0'
+          - php-version: '8.2'
+            phpunit-version: '9.3.0'
+          - php-version: '8.2'
+            phpunit-version: '9.2.0'
+          - php-version: '8.2'
+            phpunit-version: '9.1.0'
+          - php-version: '8.2'
+            phpunit-version: '9.0.0'
+          - php-version: '8.2'
+            phpunit-version: '8.4.0'
+          - php-version: '8.2'
+            phpunit-version: '8.3.0'
+          - php-version: '8.2'
+            phpunit-version: '8.2.0'
+          - php-version: '8.2'
+            phpunit-version: '8.1.0'
+          - php-version: '8.2'
+            phpunit-version: '8.0.0'
+          - php-version: '8.2'
+            phpunit-version: '7.5.0'
+          - php-version: '8.2'
+            phpunit-version: '7.4.0'
+          - php-version: '8.2'
+            phpunit-version: '7.3.0'
+          - php-version: '8.2'
+            phpunit-version: '7.2.0'
+          - php-version: '8.2'
+            phpunit-version: '7.1.0'
+          - php-version: '8.2'
+            phpunit-version: '7.0.0'
+          - php-version: '8.2'
+            phpunit-version: '6.5.0'
+          - php-version: '8.2'
+            phpunit-version: '6.4.0'
+          - php-version: '8.2'
+            phpunit-version: '6.3.0'
+          - php-version: '8.2'
+            phpunit-version: '6.2.0'
+          - php-version: '8.2'
+            phpunit-version: '6.1.0'
+          - php-version: '8.2'
+            phpunit-version: '6.0.0'
+
+          # PHP 8.1 Exclusions
+          - php-version: '8.1'
+            phpunit-version: '12.1.0'
+          - php-version: '8.1'
+            phpunit-version: '12.0.0'
+          - php-version: '8.1'
+            phpunit-version: '11.5.0'
+          - php-version: '8.1'
+            phpunit-version: '11.4.0'
+          - php-version: '8.1'
+            phpunit-version: '11.3.0'
+          - php-version: '8.1'
+            phpunit-version: '11.2.0'
+          - php-version: '8.1'
+            phpunit-version: '11.1.0'
+          - php-version: '8.1'
+            phpunit-version: '11.0.0'
+          - php-version: '8.1'
+            phpunit-version: '9.4.0'
+          - php-version: '8.1'
+            phpunit-version: '9.3.0'
+          - php-version: '8.1'
+            phpunit-version: '9.2.0'
+          - php-version: '8.1'
+            phpunit-version: '9.1.0'
+          - php-version: '8.1'
+            phpunit-version: '9.0.0'
+          - php-version: '8.1'
+            phpunit-version: '8.4.0'
+          - php-version: '8.1'
+            phpunit-version: '8.3.0'
+          - php-version: '8.1'
+            phpunit-version: '8.2.0'
+          - php-version: '8.1'
+            phpunit-version: '8.1.0'
+          - php-version: '8.1'
+            phpunit-version: '8.0.0'
+          - php-version: '8.1'
+            phpunit-version: '7.5.0'
+          - php-version: '8.1'
+            phpunit-version: '7.4.0'
+          - php-version: '8.1'
+            phpunit-version: '7.3.0'
+          - php-version: '8.1'
+            phpunit-version: '7.2.0'
+          - php-version: '8.1'
+            phpunit-version: '7.1.0'
+          - php-version: '8.1'
+            phpunit-version: '7.0.0'
+          - php-version: '8.1'
+            phpunit-version: '6.5.0'
+          - php-version: '8.1'
+            phpunit-version: '6.4.0'
+          - php-version: '8.1'
+            phpunit-version: '6.3.0'
+          - php-version: '8.1'
+            phpunit-version: '6.2.0'
+          - php-version: '8.1'
+            phpunit-version: '6.1.0'
+          - php-version: '8.1'
+            phpunit-version: '6.0.0'
+
+          # PHP 8.0 Exclusions
+          - php-version: '8.0'
+            phpunit-version: '12.1.0'
+          - php-version: '8.0'
+            phpunit-version: '12.0.0'
+          - php-version: '8.0'
+            phpunit-version: '11.5.0'
+          - php-version: '8.0'
+            phpunit-version: '11.4.0'
+          - php-version: '8.0'
+            phpunit-version: '11.3.0'
+          - php-version: '8.0'
+            phpunit-version: '11.2.0'
+          - php-version: '8.0'
+            phpunit-version: '11.1.0'
+          - php-version: '8.0'
+            phpunit-version: '11.0.0'
+          - php-version: '8.0'
+            phpunit-version: '10.5.0'
+          - php-version: '8.0'
+            phpunit-version: '10.4.0'
+          - php-version: '8.0'
+            phpunit-version: '10.3.0'
+          - php-version: '8.0'
+            phpunit-version: '10.2.0'
+          - php-version: '8.0'
+            phpunit-version: '10.1.0'
+          - php-version: '8.0'
+            phpunit-version: '10.0.0'
+          - php-version: '8.0'
+            phpunit-version: '9.2.0'
+          - php-version: '8.0'
+            phpunit-version: '9.1.0'
+          - php-version: '8.0'
+            phpunit-version: '9.0.0'
+          - php-version: '8.0'
+            phpunit-version: '8.4.0'
+          - php-version: '8.0'
+            phpunit-version: '8.3.0'
+          - php-version: '8.0'
+            phpunit-version: '8.2.0'
+          - php-version: '8.0'
+            phpunit-version: '8.1.0'
+          - php-version: '8.0'
+            phpunit-version: '8.0.0'
+          - php-version: '8.0'
+            phpunit-version: '7.5.0'
+          - php-version: '8.0'
+            phpunit-version: '7.4.0'
+          - php-version: '8.0'
+            phpunit-version: '7.3.0'
+          - php-version: '8.0'
+            phpunit-version: '7.2.0'
+          - php-version: '8.0'
+            phpunit-version: '7.1.0'
+          - php-version: '8.0'
+            phpunit-version: '7.0.0'
+          - php-version: '8.0'
+            phpunit-version: '6.5.0'
+          - php-version: '8.0'
+            phpunit-version: '6.4.0'
+          - php-version: '8.0'
+            phpunit-version: '6.3.0'
+          - php-version: '8.0'
+            phpunit-version: '6.2.0'
+          - php-version: '8.0'
+            phpunit-version: '6.1.0'
+          - php-version: '8.0'
+            phpunit-version: '6.0.0'
+            
+          # PHP 7.4 Exclusions
+          - php-version: '7.4'
+            phpunit-version: '12.1.0'
+          - php-version: '7.4'
+            phpunit-version: '12.0.0'
+          - php-version: '7.4'
+            phpunit-version: '11.5.0'
+          - php-version: '7.4'
+            phpunit-version: '11.4.0'
+          - php-version: '7.4'
+            phpunit-version: '11.3.0'
+          - php-version: '7.4'
+            phpunit-version: '11.2.0'
+          - php-version: '7.4'
+            phpunit-version: '11.1.0'
+          - php-version: '7.4'
+            phpunit-version: '11.0.0'
+          - php-version: '7.4'
+            phpunit-version: '10.5.0'
+          - php-version: '7.4'
+            phpunit-version: '10.4.0'
+          - php-version: '7.4'
+            phpunit-version: '10.3.0'
+          - php-version: '7.4'
+            phpunit-version: '10.2.0'
+          - php-version: '7.4'
+            phpunit-version: '10.1.0'
+          - php-version: '7.4'
+            phpunit-version: '10.0.0'
+          - php-version: '7.4'
+            phpunit-version: '8.1.0'
+          - php-version: '7.4'
+            phpunit-version: '8.0.0'
+          - php-version: '7.4'
+            phpunit-version: '7.4.0'
+          - php-version: '7.4'
+            phpunit-version: '7.3.0'
+          - php-version: '7.4'
+            phpunit-version: '7.2.0'
+          - php-version: '7.4'
+            phpunit-version: '7.1.0'
+          - php-version: '7.4'
+            phpunit-version: '7.0.0'
+          - php-version: '7.4'
+            phpunit-version: '6.5.0'
+          - php-version: '7.4'
+            phpunit-version: '6.4.0'
+          - php-version: '7.4'
+            phpunit-version: '6.3.0'
+          - php-version: '7.4'
+            phpunit-version: '6.2.0'
+          - php-version: '7.4'
+            phpunit-version: '6.1.0'
+          - php-version: '7.4'
+            phpunit-version: '6.0.0'
+
+          # PHP 7.3 Exclusions
+          - php-version: '7.3'
+            phpunit-version: '12.1.0'
+          - php-version: '7.3'
+            phpunit-version: '12.0.0'
+          - php-version: '7.3'
+            phpunit-version: '11.5.0'
+          - php-version: '7.3'
+            phpunit-version: '11.4.0'
+          - php-version: '7.3'
+            phpunit-version: '11.3.0'
+          - php-version: '7.3'
+            phpunit-version: '11.2.0'
+          - php-version: '7.3'
+            phpunit-version: '11.1.0'
+          - php-version: '7.3'
+            phpunit-version: '11.0.0'
+          - php-version: '7.3'
+            phpunit-version: '10.5.0'
+          - php-version: '7.3'
+            phpunit-version: '10.4.0'
+          - php-version: '7.3'
+            phpunit-version: '10.3.0'
+          - php-version: '7.3'
+            phpunit-version: '10.2.0'
+          - php-version: '7.3'
+            phpunit-version: '10.1.0'
+          - php-version: '7.3'
+            phpunit-version: '10.0.0'
+          
+          # PHP 7.2 Exclusions
+          - php-version: '7.2'
+            phpunit-version: '12.1.0'
+          - php-version: '7.2'
+            phpunit-version: '12.0.0'
+          - php-version: '7.2'
+            phpunit-version: '11.5.0'
+          - php-version: '7.2'
+            phpunit-version: '11.4.0'
+          - php-version: '7.2'
+            phpunit-version: '11.3.0'
+          - php-version: '7.2'
+            phpunit-version: '11.2.0'
+          - php-version: '7.2'
+            phpunit-version: '11.1.0'
+          - php-version: '7.2'
+            phpunit-version: '11.0.0'
+          - php-version: '7.2'
+            phpunit-version: '10.5.0'
+          - php-version: '7.2'
+            phpunit-version: '10.4.0'
+          - php-version: '7.2'
+            phpunit-version: '10.3.0'
+          - php-version: '7.2'
+            phpunit-version: '10.2.0'
+          - php-version: '7.2'
+            phpunit-version: '10.1.0'
+          - php-version: '7.2'
+            phpunit-version: '10.0.0'
+          - php-version: '7.2'
+            phpunit-version: '9.6.0'
+          - php-version: '7.2'
+            phpunit-version: '9.5.0'
+          - php-version: '7.2'
+            phpunit-version: '9.4.0'
+          - php-version: '7.2'
+            phpunit-version: '9.3.0'
+          - php-version: '7.2'
+            phpunit-version: '9.2.0'
+          - php-version: '7.2'
+            phpunit-version: '9.1.0'
+          - php-version: '7.2'
+            phpunit-version: '9.0.0'
+          
+          # PHP 7.1 Exclusions
+          - php-version: '7.1'
+            phpunit-version: '12.1.0'
+          - php-version: '7.1'
+            phpunit-version: '12.0.0'
+          - php-version: '7.1'
+            phpunit-version: '11.5.0'
+          - php-version: '7.1'
+            phpunit-version: '11.4.0'
+          - php-version: '7.1'
+            phpunit-version: '11.3.0'
+          - php-version: '7.1'
+            phpunit-version: '11.2.0'
+          - php-version: '7.1'
+            phpunit-version: '11.1.0'
+          - php-version: '7.1'
+            phpunit-version: '11.0.0'
+          - php-version: '7.1'
+            phpunit-version: '10.5.0'
+          - php-version: '7.1'
+            phpunit-version: '10.4.0'
+          - php-version: '7.1'
+            phpunit-version: '10.3.0'
+          - php-version: '7.1'
+            phpunit-version: '10.2.0'
+          - php-version: '7.1'
+            phpunit-version: '10.1.0'
+          - php-version: '7.1'
+            phpunit-version: '10.0.0'
+          - php-version: '7.1'
+            phpunit-version: '9.6.0'
+          - php-version: '7.1'
+            phpunit-version: '9.5.0'
+          - php-version: '7.1'
+            phpunit-version: '9.4.0'
+          - php-version: '7.1'
+            phpunit-version: '9.3.0'
+          - php-version: '7.1'
+            phpunit-version: '9.2.0'
+          - php-version: '7.1'
+            phpunit-version: '9.1.0'
+          - php-version: '7.1'
+            phpunit-version: '9.0.0'
+          - php-version: '7.1'
+            phpunit-version: '8.5.0'
+          - php-version: '7.1'
+            phpunit-version: '8.4.0'
+          - php-version: '7.1'
+            phpunit-version: '8.3.0'
+          - php-version: '7.1'
+            phpunit-version: '8.2.0'
+          - php-version: '7.1'
+            phpunit-version: '8.1.0'
+          - php-version: '7.1'
+            phpunit-version: '8.0.0'
+      
+          # PHP 7.0 Exclusions
+          - php-version: '7.0'
+            phpunit-version: '12.1.0'
+          - php-version: '7.0'
+            phpunit-version: '12.0.0'
+          - php-version: '7.0'
+            phpunit-version: '11.5.0'
+          - php-version: '7.0'
+            phpunit-version: '11.4.0'
+          - php-version: '7.0'
+            phpunit-version: '11.3.0'
+          - php-version: '7.0'
+            phpunit-version: '11.2.0'
+          - php-version: '7.0'
+            phpunit-version: '11.1.0'
+          - php-version: '7.0'
+            phpunit-version: '11.0.0'
+          - php-version: '7.0'
+            phpunit-version: '10.5.0'
+          - php-version: '7.0'
+            phpunit-version: '10.4.0'
+          - php-version: '7.0'
+            phpunit-version: '10.3.0'
+          - php-version: '7.0'
+            phpunit-version: '10.2.0'
+          - php-version: '7.0'
+            phpunit-version: '10.1.0'
+          - php-version: '7.0'
+            phpunit-version: '10.0.0'
+          - php-version: '7.0'
+            phpunit-version: '9.6.0'
+          - php-version: '7.0'
+            phpunit-version: '9.5.0'
+          - php-version: '7.0'
+            phpunit-version: '9.4.0'
+          - php-version: '7.0'
+            phpunit-version: '9.3.0'
+          - php-version: '7.0'
+            phpunit-version: '9.2.0'
+          - php-version: '7.0'
+            phpunit-version: '9.1.0'
+          - php-version: '7.0'
+            phpunit-version: '9.0.0'
+          - php-version: '7.0'
+            phpunit-version: '8.5.0'
+          - php-version: '7.0'
+            phpunit-version: '8.4.0'
+          - php-version: '7.0'
+            phpunit-version: '8.3.0'
+          - php-version: '7.0'
+            phpunit-version: '8.2.0'
+          - php-version: '7.0'
+            phpunit-version: '8.1.0'
+          - php-version: '7.0'
+            phpunit-version: '8.0.0'
+          - php-version: '7.0'
+            phpunit-version: '7.5.0'
+          - php-version: '7.0'
+            phpunit-version: '7.4.0'
+          - php-version: '7.0'
+            phpunit-version: '7.3.0'
+          - php-version: '7.0'
+            phpunit-version: '7.2.0'
+          - php-version: '7.0'
+            phpunit-version: '7.1.0'
+          - php-version: '7.0'
+            phpunit-version: '7.0.0'
+
+    name: PHPUnit ${{ matrix.phpunit-version }} on PHP ${{ matrix.php-version }}
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Install PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.php-version }}
+
+      - name: Install Dependencies
+        run: composer require phpunit/phpunit:~${{ matrix.phpunit-version }} --no-interaction --no-progress
+
+      - name: PHPUnit
+        run: vendor/bin/phpunit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9c9d0d9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/.phpunit.result.cache
+/composer.lock
+/vendor/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 349d29e..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-language: php
-
-sudo: false
-
-cache:
-  directories:
-    - $HOME/.composer/cache
-
-env:
-  - PHPUNIT_VERSION=dev-master
-  - PHPUNIT_VERSION=~5.7.0
-  - PHPUNIT_VERSION=~5.6.0
-  - PHPUNIT_VERSION=~5.5.0
-  - PHPUNIT_VERSION=~5.4.0
-  - PHPUNIT_VERSION=~5.3.0
-  - PHPUNIT_VERSION=~5.2.0
-  - PHPUNIT_VERSION=~5.1.0
-  - PHPUNIT_VERSION=~5.0.0
-  - PHPUNIT_VERSION=~4.8.0
-  - PHPUNIT_VERSION=~4.7.0
-  - PHPUNIT_VERSION=~4.6.0
-  - PHPUNIT_VERSION=~4.5.0
-  - PHPUNIT_VERSION=~4.4.0
-  - PHPUNIT_VERSION=~4.3.0
-  - PHPUNIT_VERSION=~4.2.0
-  - PHPUNIT_VERSION=~4.1.0
-  - PHPUNIT_VERSION=~4.0.0
-
-php:
-  - 7.1
-  - 7.0
-  - 5.6
-  - 5.5
-  - hhvm
-
-matrix:
-  fast_finish: true
-  allow_failures:
-    - php: hhvm
-    - env: PHPUNIT_VERSION=dev-master
-  exclude:
-    - env: PHPUNIT_VERSION=~5.0.0
-      php: 5.5
-    - env: PHPUNIT_VERSION=~5.1.0
-      php: 5.5
-    - env: PHPUNIT_VERSION=~5.2.0
-      php: 5.5
-    - env: PHPUNIT_VERSION=~5.3.0
-      php: 5.5
-    - env: PHPUNIT_VERSION=~5.4.0
-      php: 5.5
-    - env: PHPUNIT_VERSION=~5.5.0
-      php: 5.5
-    - env: PHPUNIT_VERSION=~5.6.0
-      php: 5.5
-    - env: PHPUNIT_VERSION=~5.7.0
-      php: 5.5
-    - env: PHPUNIT_VERSION=dev-master
-      php: 5.5
-
-install:
-  - composer require phpunit/phpunit:${PHPUNIT_VERSION}
-  - composer require squizlabs/php_codesniffer
-  - composer require phpmd/phpmd
-
-script:
-  - vendor/bin/phpunit
-  - vendor/bin/phpcs --standard=PSR2 classes/ tests/
-  - vendor/bin/phpmd classes/ text cleancode,codesize,controversial,design,naming,unusedcode
-
diff --git a/README.md b/README.md
index cbd403c..016a872 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,11 @@
+[![.github/workflows/tests.yml](https://github.com/php-mock/php-mock-phpunit/actions/workflows/tests.yml/badge.svg)](https://github.com/php-mock/php-mock-phpunit/actions/workflows/tests.yml)
+
 # Mock PHP built-in functions with PHPUnit
 
 This package integrates the function mock library
 [PHP-Mock](https://github.com/php-mock/php-mock) with PHPUnit.
 
-# Installation
+## Installation
 
 Use [Composer](https://getcomposer.org/):
 
@@ -11,7 +13,7 @@ Use [Composer](https://getcomposer.org/):
 composer require --dev php-mock/php-mock-phpunit
 ```
 
-# Usage
+## Usage
 
 PHP-Mock integrates with the trait
 [`PHPMock`](http://php-mock.github.io/php-mock-phpunit/api/class-phpmock.phpunit.PHPMock.html)
@@ -22,16 +24,12 @@ With this method you can build a mock in the way you are used to build a
 PHPUnit mock:
 
 ```php
-<?php
-
 namespace foo;
 
-use phpmock\phpunit\PHPMock;
-
-class BuiltinTest extends \PHPUnit_Framework_TestCase
+class BuiltinTest extends \PHPUnit\Framework\TestCase
 {
 
-    use PHPMock;
+    use \phpmock\phpunit\PHPMock;
 
     public function testTime()
     {
@@ -62,7 +60,7 @@ class BuiltinTest extends \PHPUnit_Framework_TestCase
 There's no need to disable the mocked function. The PHPUnit integration does
 that for you.
 
-## Restrictions
+### Restrictions
 
 This library comes with the same restrictions as the underlying
 [`php-mock`](https://github.com/php-mock/php-mock#requirements-and-restrictions):
@@ -79,15 +77,12 @@ This library comes with the same restrictions as the underlying
   This would define a side effectless namespaced function. Another effective
   approach is running your test in an isolated process (e.g. with `@runInSeparateProcess`).
 
-# License and authors
+## License and authors
 
 This project is free and under the WTFPL.
 Responsable for this project is Markus Malkusch markus@malkusch.de.
 
-## Donations
+### Donations
 
 If you like this project and feel generous donate a few Bitcoins here:
 [1335STSwu9hST4vcMRppEPgENMHD2r1REK](bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK)
-
-[![Build Status](https://travis-ci.org/php-mock/php-mock-phpunit.svg?branch=master)](https://travis-ci.org/php-mock/php-mock-phpunit)
-
diff --git a/autoload.php b/autoload.php
new file mode 100644
index 0000000..e16fa67
--- /dev/null
+++ b/autoload.php
@@ -0,0 +1,122 @@
+<?php
+
+if (class_exists(\PHPUnit\Framework\MockObject\Rule\InvocationOrder::class)) {
+    class_alias(
+        \PHPUnit\Framework\MockObject\Rule\InvocationOrder::class,
+        \PHPUnit\Framework\MockObject\Matcher\Invocation::class
+    );
+} elseif (! interface_exists(\PHPUnit\Framework\MockObject\Matcher\Invocation::class)) {
+    class_alias(
+        \PHPUnit_Framework_MockObject_Matcher_Invocation::class,
+        \PHPUnit\Framework\MockObject\Matcher\Invocation::class
+    );
+}
+
+if (! interface_exists(\PHPUnit\Framework\MockObject\Invocation::class)
+    && interface_exists(\PHPUnit_Framework_MockObject_Invocation::class)
+) {
+    class_alias(
+        \PHPUnit_Framework_MockObject_Invocation::class,
+        \PHPUnit\Framework\MockObject\Invocation::class
+    );
+}
+
+if (! interface_exists(\PHPUnit\Framework\MockObject\MockObject::class)) {
+    class_alias(
+        \PHPUnit_Framework_MockObject_MockObject::class,
+        \PHPUnit\Framework\MockObject\MockObject::class
+    );
+}
+
+if (! class_exists(\PHPUnit\Framework\MockObject\Builder\InvocationMocker::class)) {
+    if (class_exists(\PHPUnit\Framework\MockObject\InvocationStubberImplementation::class)) {
+        class_alias(
+            \PHPUnit\Framework\MockObject\InvocationStubberImplementation::class,
+            \PHPUnit\Framework\MockObject\Builder\InvocationMocker::class
+        );
+    } else {
+        class_alias(
+            \PHPUnit_Framework_MockObject_Builder_InvocationMocker::class,
+            \PHPUnit\Framework\MockObject\Builder\InvocationMocker::class
+        );
+    }
+}
+
+if (class_exists(\PHPUnit\Framework\MockObject\Rule\MethodName::class)) {
+    class_alias(
+        \PHPUnit\Framework\MockObject\Rule\MethodName::class,
+        \PHPUnit\Framework\MockObject\Matcher\MethodName::class
+    );
+}
+
+if (! class_exists(\PHPUnit\Framework\MockObject\Matcher\MethodName::class)) {
+    class_alias(
+        \PHPUnit_Framework_MockObject_Matcher_MethodName::class,
+        \PHPUnit\Framework\MockObject\Matcher\MethodName::class
+    );
+}
+
+if (!class_exists(\PHPUnit\Framework\MockObject\InvocationHandler::class)
+    && !interface_exists(\PHPUnit\Framework\MockObject\Stub\MatcherCollection::class)
+) {
+    class_alias(
+        \PHPUnit_Framework_MockObject_Stub_MatcherCollection::class,
+        \PHPUnit\Framework\MockObject\Stub\MatcherCollection::class
+    );
+}
+
+if (!class_exists(\PHPUnit\Framework\MockObject\InvocationHandler::class)
+    && !class_exists(\PHPUnit\Framework\MockObject\InvocationMocker::class)
+) {
+    class_alias(
+        \PHPUnit_Framework_MockObject_InvocationMocker::class,
+        \PHPUnit\Framework\MockObject\InvocationMocker::class
+    );
+}
+
+$hasVersion = class_exists(\PHPUnit\Runner\Version::class);
+
+if ($hasVersion
+    && version_compare(\PHPUnit\Runner\Version::id(), '10.0.0') >= 0
+) {
+    class_alias(
+        phpmock\phpunit\MockDisablerPHPUnit10::class,
+        phpmock\phpunit\MockDisabler::class
+    );
+} elseif (! class_exists(\PHPUnit\Framework\BaseTestListener::class)) {
+    include __DIR__ . '/compatibility/BaseTestListener.php';
+    class_alias(
+        phpmock\phpunit\MockDisablerPHPUnit7::class,
+        phpmock\phpunit\MockDisabler::class
+    );
+} else {
+    class_alias(
+        phpmock\phpunit\MockDisablerPHPUnit6::class,
+        phpmock\phpunit\MockDisabler::class
+    );
+}
+
+if ($hasVersion
+    && version_compare(\PHPUnit\Runner\Version::id(), '12.0.0') >= 0
+) {
+    class_alias(\phpmock\phpunit\DefaultArgumentRemoverReturnTypes120::class, \phpmock\phpunit\DefaultArgumentRemover::class);
+    class_alias(\phpmock\phpunit\MockObjectProxyReturnTypes120::class, \phpmock\phpunit\MockObjectProxy::class);
+} elseif ($hasVersion
+    && version_compare(\PHPUnit\Runner\Version::id(), '10.0.0') >= 0
+) {
+    class_alias(\phpmock\phpunit\DefaultArgumentRemoverReturnTypes100::class, \phpmock\phpunit\DefaultArgumentRemover::class);
+    class_alias(\phpmock\phpunit\MockObjectProxyReturnTypes100::class, \phpmock\phpunit\MockObjectProxy::class);
+} elseif ($hasVersion
+    && version_compare(\PHPUnit\Runner\Version::id(), '8.4.0') >= 0
+) {
+    class_alias(\phpmock\phpunit\DefaultArgumentRemoverReturnTypes84::class, \phpmock\phpunit\DefaultArgumentRemover::class);
+    class_alias(\phpmock\phpunit\MockObjectProxyReturnTypes84::class, \phpmock\phpunit\MockObjectProxy::class);
+} elseif ($hasVersion
+    && version_compare(\PHPUnit\Runner\Version::id(), '8.1.0') >= 0
+) {
+    class_alias(\phpmock\phpunit\DefaultArgumentRemoverReturnTypes::class, \phpmock\phpunit\DefaultArgumentRemover::class);
+    class_alias(\phpmock\phpunit\MockObjectProxyReturnTypes::class, \phpmock\phpunit\MockObjectProxy::class);
+} else {
+    class_alias(\phpmock\phpunit\DefaultArgumentRemoverNoReturnTypes::class, \phpmock\phpunit\DefaultArgumentRemover::class);
+    class_alias(\phpmock\phpunit\MockObjectProxyNoReturnTypes::class, \phpmock\phpunit\MockObjectProxy::class);
+}
diff --git a/classes/DefaultArgumentRemover.php b/classes/DefaultArgumentRemover.php
deleted file mode 100644
index 1b0f15e..0000000
--- a/classes/DefaultArgumentRemover.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-namespace phpmock\phpunit;
-
-use phpmock\generator\MockFunctionGenerator;
-
-/**
- * Removes default arguments from the invocation.
- *
- * @author Markus Malkusch <markus@malkusch.de>
- * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
- * @license http://www.wtfpl.net/txt/copying/ WTFPL
- * @internal
- */
-class DefaultArgumentRemover implements \PHPUnit_Framework_MockObject_Matcher_Invocation
-{
-
-    /**
-     * @SuppressWarnings(PHPMD)
-     */
-    public function invoked(\PHPUnit_Framework_MockObject_Invocation $invocation)
-    {
-    }
-
-    /**
-     * @SuppressWarnings(PHPMD)
-     */
-    public function matches(\PHPUnit_Framework_MockObject_Invocation $invocation)
-    {
-        MockFunctionGenerator::removeDefaultArguments($invocation->parameters);
-        return false;
-    }
-
-    public function verify()
-    {
-    }
-    
-    /**
-     * This method is not defined in the interface, but used in
-     * PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers().
-     *
-     * @return boolean
-     * @see \PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers()
-     */
-    public function hasMatchers()
-    {
-        return false;
-    }
-
-    public function toString()
-    {
-        return __CLASS__;
-    }
-}
diff --git a/classes/DefaultArgumentRemoverNoReturnTypes.php b/classes/DefaultArgumentRemoverNoReturnTypes.php
new file mode 100644
index 0000000..84c1abe
--- /dev/null
+++ b/classes/DefaultArgumentRemoverNoReturnTypes.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use phpmock\generator\MockFunctionGenerator;
+use PHPUnit\Framework\MockObject\Invocation;
+use PHPUnit\Framework\MockObject\Matcher\Invocation as InvocationInterface;
+
+/**
+ * Removes default arguments from the invocation.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class DefaultArgumentRemoverNoReturnTypes implements InvocationInterface
+{
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function invoked(Invocation $invocation)
+    {
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function matches(Invocation $invocation)
+    {
+        if ($invocation instanceof Invocation\StaticInvocation) {
+            $this->removeDefaultArguments($invocation);
+        } else {
+            MockFunctionGenerator::removeDefaultArguments($invocation->parameters);
+        }
+
+        return false;
+    }
+
+    public function verify()
+    {
+    }
+    
+    /**
+     * This method is not defined in the interface, but used in
+     * PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers().
+     *
+     * @return boolean
+     * @see \PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers()
+     */
+    public function hasMatchers()
+    {
+        return false;
+    }
+
+    public function toString() : string
+    {
+        return __CLASS__;
+    }
+
+    /**
+     * Remove default arguments from StaticInvocation or its children (hack)
+     *
+     * @SuppressWarnings(PHPMD)
+     */
+    private function removeDefaultArguments(Invocation\StaticInvocation $invocation)
+    {
+        $remover = function () {
+            MockFunctionGenerator::removeDefaultArguments($this->parameters);
+        };
+
+        $remover->bindTo($invocation, Invocation\StaticInvocation::class)();
+    }
+}
diff --git a/classes/DefaultArgumentRemoverReturnTypes.php b/classes/DefaultArgumentRemoverReturnTypes.php
new file mode 100644
index 0000000..32d928d
--- /dev/null
+++ b/classes/DefaultArgumentRemoverReturnTypes.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use phpmock\generator\MockFunctionGenerator;
+use PHPUnit\Framework\MockObject\Invocation;
+use PHPUnit\Framework\MockObject\Matcher\Invocation as InvocationInterface;
+
+/**
+ * Removes default arguments from the invocation.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class DefaultArgumentRemoverReturnTypes implements InvocationInterface
+{
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function invoked(Invocation $invocation)
+    {
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function matches(Invocation $invocation) : bool
+    {
+        $iClass = class_exists(Invocation::class);
+
+        if ($iClass
+            || $invocation instanceof Invocation\StaticInvocation
+        ) {
+            $this->removeDefaultArguments(
+                $invocation,
+                $iClass ? Invocation::class : Invocation\StaticInvocation::class
+            );
+        } else {
+            MockFunctionGenerator::removeDefaultArguments($invocation->parameters);
+        }
+
+        return false;
+    }
+
+    public function verify() : void
+    {
+    }
+    
+    /**
+     * This method is not defined in the interface, but used in
+     * PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers().
+     *
+     * @return boolean
+     * @see \PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers()
+     */
+    public function hasMatchers()
+    {
+        return false;
+    }
+
+    public function toString() : string
+    {
+        return __CLASS__;
+    }
+
+    /**
+     * Remove default arguments from StaticInvocation or its children (hack)
+     *
+     * @SuppressWarnings(PHPMD)
+     */
+    private function removeDefaultArguments(Invocation $invocation, string $class)
+    {
+        $remover = function () {
+            MockFunctionGenerator::removeDefaultArguments($this->parameters);
+        };
+
+        $remover->bindTo($invocation, $class)();
+    }
+}
diff --git a/classes/DefaultArgumentRemoverReturnTypes100.php b/classes/DefaultArgumentRemoverReturnTypes100.php
new file mode 100644
index 0000000..c32dcbb
--- /dev/null
+++ b/classes/DefaultArgumentRemoverReturnTypes100.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use phpmock\generator\MockFunctionGenerator;
+use PHPUnit\Framework\MockObject\Invocation;
+use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
+use ReflectionClass;
+
+/**
+ * Removes default arguments from the invocation.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class DefaultArgumentRemoverReturnTypes100 extends InvocationOrder
+{
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function invokedDo(Invocation $invocation): void
+    {
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function matches(Invocation $invocation) : bool
+    {
+        $iClass = class_exists(Invocation::class);
+
+        if ($iClass
+            || $invocation instanceof Invocation\StaticInvocation
+        ) {
+            $this->removeDefaultArguments(
+                $invocation,
+                $iClass ? Invocation::class : Invocation\StaticInvocation::class
+            );
+        } elseif (!$this->shouldRemoveDefaultArgumentsWithReflection($invocation)) {
+            MockFunctionGenerator::removeDefaultArguments($invocation->parameters);
+        }
+
+        return false;
+    }
+
+    public function verify() : void
+    {
+    }
+
+    /**
+     * This method is not defined in the interface, but used in
+     * PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers().
+     *
+     * @return boolean
+     * @see \PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers()
+     */
+    public function hasMatchers()
+    {
+        return false;
+    }
+
+    public function toString() : string
+    {
+        return __CLASS__;
+    }
+
+    /**
+     * Remove default arguments from StaticInvocation or its children (hack)
+     *
+     * @SuppressWarnings(PHPMD)
+     */
+    private function removeDefaultArguments(Invocation $invocation, string $class)
+    {
+        if ($this->shouldRemoveDefaultArgumentsWithReflection($invocation)) {
+            return;
+        }
+
+        $remover = function () {
+            MockFunctionGenerator::removeDefaultArguments($this->parameters);
+        };
+
+        $remover->bindTo($invocation, $class)();
+    }
+
+    /**
+     * Alternative to remove default arguments from StaticInvocation or its children (hack)
+     *
+     * @SuppressWarnings(PHPMD.StaticAccess)
+     */
+    public static function removeDefaultArgumentsWithReflection(Invocation $invocation): Invocation
+    {
+        if (!(new self())->shouldRemoveDefaultArgumentsWithReflection($invocation)) {
+            return $invocation;
+        }
+
+        $reflection = new ReflectionClass($invocation);
+
+        $reflectionReturnType = $reflection->getProperty('returnType');
+        $reflectionReturnType->setAccessible(true);
+
+        $reflectionIsOptional = $reflection->getProperty('isReturnTypeNullable');
+        $reflectionIsOptional->setAccessible(true);
+
+        $reflectionIsProxied = $reflection->getProperty('proxiedCall');
+        $reflectionIsProxied->setAccessible(true);
+
+        $returnType = $reflectionReturnType->getValue($invocation);
+        $proxiedCall = $reflectionIsProxied->getValue($invocation);
+
+        if ($reflectionIsOptional->getValue($invocation)) {
+            $returnType = '?' . $returnType;
+        }
+
+        $parameters = $invocation->parameters();
+        MockFunctionGenerator::removeDefaultArguments($parameters);
+
+        return new Invocation(
+            $invocation->className(),
+            $invocation->methodName(),
+            $parameters,
+            $returnType,
+            $invocation->object(),
+            false,
+            $proxiedCall
+        );
+    }
+
+    protected function shouldRemoveDefaultArgumentsWithReflection(Invocation $invocation)
+    {
+        return method_exists($invocation, 'parameters');
+    }
+}
diff --git a/classes/DefaultArgumentRemoverReturnTypes120.php b/classes/DefaultArgumentRemoverReturnTypes120.php
new file mode 100644
index 0000000..8cce8fa
--- /dev/null
+++ b/classes/DefaultArgumentRemoverReturnTypes120.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use phpmock\generator\MockFunctionGenerator;
+use PHPUnit\Framework\MockObject\Invocation;
+use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
+use ReflectionClass;
+
+/**
+ * Removes default arguments from the invocation.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class DefaultArgumentRemoverReturnTypes120 extends InvocationOrder
+{
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function invokedDo(Invocation $invocation): void
+    {
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function matches(Invocation $invocation) : bool
+    {
+        $iClass = class_exists(Invocation::class);
+
+        if ($iClass
+            || $invocation instanceof Invocation\StaticInvocation
+        ) {
+            $this->removeDefaultArguments(
+                $invocation,
+                $iClass ? Invocation::class : Invocation\StaticInvocation::class
+            );
+        } elseif (!$this->shouldRemoveDefaultArgumentsWithReflection($invocation)) {
+            MockFunctionGenerator::removeDefaultArguments($invocation->parameters);
+        }
+
+        return false;
+    }
+
+    public function verify() : void
+    {
+    }
+
+    /**
+     * This method is not defined in the interface, but used in
+     * PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers().
+     *
+     * @return boolean
+     * @see \PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers()
+     */
+    public function hasMatchers()
+    {
+        return false;
+    }
+
+    public function toString() : string
+    {
+        return __CLASS__;
+    }
+
+    /**
+     * Remove default arguments from StaticInvocation or its children (hack)
+     *
+     * @SuppressWarnings(PHPMD)
+     */
+    private function removeDefaultArguments(Invocation $invocation, string $class)
+    {
+        if ($this->shouldRemoveDefaultArgumentsWithReflection($invocation)) {
+            return;
+        }
+
+        $remover = function () {
+            MockFunctionGenerator::removeDefaultArguments($this->parameters);
+        };
+
+        $remover->bindTo($invocation, $class)();
+    }
+
+    /**
+     * Alternative to remove default arguments from StaticInvocation or its children (hack)
+     *
+     * @SuppressWarnings(PHPMD.StaticAccess)
+     */
+    public static function removeDefaultArgumentsWithReflection(Invocation $invocation): Invocation
+    {
+        if (!(new self())->shouldRemoveDefaultArgumentsWithReflection($invocation)) {
+            return $invocation;
+        }
+
+        $reflection = new ReflectionClass($invocation);
+
+        $reflectionReturnType = $reflection->getProperty('returnType');
+        $reflectionReturnType->setAccessible(true);
+
+        $reflectionIsOptional = $reflection->getProperty('isReturnTypeNullable');
+        $reflectionIsOptional->setAccessible(true);
+
+        $returnType = $reflectionReturnType->getValue($invocation);
+
+        if ($reflectionIsOptional->getValue($invocation)) {
+            $returnType = '?' . $returnType;
+        }
+
+        $parameters = $invocation->parameters();
+        MockFunctionGenerator::removeDefaultArguments($parameters);
+
+        return new Invocation(
+            $invocation->className(),
+            $invocation->methodName(),
+            $parameters,
+            $returnType,
+            $invocation->object()
+        );
+    }
+
+    protected function shouldRemoveDefaultArgumentsWithReflection(Invocation $invocation)
+    {
+        return method_exists($invocation, 'parameters');
+    }
+}
diff --git a/classes/DefaultArgumentRemoverReturnTypes84.php b/classes/DefaultArgumentRemoverReturnTypes84.php
new file mode 100644
index 0000000..4513ef5
--- /dev/null
+++ b/classes/DefaultArgumentRemoverReturnTypes84.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use phpmock\generator\MockFunctionGenerator;
+use PHPUnit\Framework\MockObject\Invocation;
+use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
+
+/**
+ * Removes default arguments from the invocation.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class DefaultArgumentRemoverReturnTypes84 extends InvocationOrder
+{
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function invokedDo(Invocation $invocation)
+    {
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function matches(Invocation $invocation) : bool
+    {
+        $iClass = class_exists(Invocation::class);
+
+        if ($iClass
+            || $invocation instanceof Invocation\StaticInvocation
+        ) {
+            $this->removeDefaultArguments(
+                $invocation,
+                $iClass ? Invocation::class : Invocation\StaticInvocation::class
+            );
+        } else {
+            MockFunctionGenerator::removeDefaultArguments($invocation->parameters);
+        }
+
+        return false;
+    }
+
+    public function verify() : void
+    {
+    }
+
+    /**
+     * This method is not defined in the interface, but used in
+     * PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers().
+     *
+     * @return boolean
+     * @see \PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers()
+     */
+    public function hasMatchers()
+    {
+        return false;
+    }
+
+    public function toString() : string
+    {
+        return __CLASS__;
+    }
+
+    /**
+     * Remove default arguments from StaticInvocation or its children (hack)
+     *
+     * @SuppressWarnings(PHPMD)
+     */
+    private function removeDefaultArguments(Invocation $invocation, string $class)
+    {
+        $remover = function () {
+            MockFunctionGenerator::removeDefaultArguments($this->parameters);
+        };
+
+        $remover->bindTo($invocation, $class)();
+    }
+}
diff --git a/classes/MockDisablerPHPUnit10.php b/classes/MockDisablerPHPUnit10.php
new file mode 100644
index 0000000..853e387
--- /dev/null
+++ b/classes/MockDisablerPHPUnit10.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use Closure;
+use phpmock\Deactivatable;
+use PHPUnit\Event\Test\Finished;
+use PHPUnit\Event\Test\FinishedSubscriber;
+
+/**
+ * Test listener for PHPUnit integration.
+ *
+ * This class disables mock functions after a test was run.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class MockDisablerPHPUnit10 implements FinishedSubscriber
+{
+    /**
+     * @var Deactivatable The function mocks.
+     */
+    private $deactivatable;
+
+    /**
+     * @var Closure|null The callback to execute after the test.
+     */
+    private $callback;
+    
+    /**
+     * Sets the function mocks.
+     *
+     * @param Deactivatable $deactivatable The function mocks.
+     * @param Closure|null $callback       The callback to execute after the test.
+     */
+    public function __construct(Deactivatable $deactivatable, ?Closure $callback = null)
+    {
+        $this->deactivatable = $deactivatable;
+        $this->callback = $callback;
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    public function notify(Finished $event) : void
+    {
+        $this->deactivatable->disable();
+        if ($this->callback !== null) {
+            ($this->callback)($this);
+        }
+    }
+
+    public function endTest(): void
+    {
+        $this->deactivatable->disable();
+        if ($this->callback !== null) {
+            ($this->callback)($this);
+        }
+    }
+}
diff --git a/classes/MockDisabler.php b/classes/MockDisablerPHPUnit6.php
similarity index 55%
rename from classes/MockDisabler.php
rename to classes/MockDisablerPHPUnit6.php
index 4cb88b8..c2926ab 100644
--- a/classes/MockDisabler.php
+++ b/classes/MockDisablerPHPUnit6.php
@@ -2,7 +2,10 @@
 
 namespace phpmock\phpunit;
 
+use Closure;
 use phpmock\Deactivatable;
+use PHPUnit\Framework\BaseTestListener;
+use PHPUnit\Framework\Test;
 
 /**
  * Test listener for PHPUnit integration.
@@ -14,36 +17,45 @@
  * @license http://www.wtfpl.net/txt/copying/ WTFPL
  * @internal
  */
-class MockDisabler extends \PHPUnit_Framework_BaseTestListener
+class MockDisablerPHPUnit6 extends BaseTestListener
 {
-
     /**
      * @var Deactivatable The function mocks.
      */
     private $deactivatable;
+
+    /**
+     * @var Closure|null The callback to execute after the test.
+     */
+    private $callback;
     
     /**
      * Sets the function mocks.
      *
      * @param Deactivatable $deactivatable The function mocks.
+     * @param Closure|null $callback       The callback to execute after the test.
      */
-    public function __construct(Deactivatable $deactivatable)
+    public function __construct(Deactivatable $deactivatable, Closure $callback = null)
     {
         $this->deactivatable = $deactivatable;
+        $this->callback = $callback;
     }
     
     /**
      * Disables the function mocks.
      *
-     * @param \PHPUnit_Framework_Test $test The test.
-     * @param int                     $time The test duration.
+     * @param Test $test The test.
+     * @param int  $time The test duration.
      *
      * @see Mock::disable()
      */
-    public function endTest(\PHPUnit_Framework_Test $test, $time)
+    public function endTest(Test $test, $time)
     {
         parent::endTest($test, $time);
         
         $this->deactivatable->disable();
+        if ($this->callback !== null) {
+            ($this->callback)($this);
+        }
     }
 }
diff --git a/classes/MockDisablerPHPUnit7.php b/classes/MockDisablerPHPUnit7.php
new file mode 100644
index 0000000..41def45
--- /dev/null
+++ b/classes/MockDisablerPHPUnit7.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use Closure;
+use phpmock\Deactivatable;
+use PHPUnit\Framework\BaseTestListener;
+use PHPUnit\Framework\Test;
+
+/**
+ * Test listener for PHPUnit integration.
+ *
+ * This class disables mock functions after a test was run.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class MockDisablerPHPUnit7 extends BaseTestListener
+{
+    /**
+     * @var Deactivatable The function mocks.
+     */
+    private $deactivatable;
+
+    /**
+     * @var Closure|null The callback to execute after the test.
+     */
+    private $callback;
+    
+    /**
+     * Sets the function mocks.
+     *
+     * @param Deactivatable $deactivatable The function mocks.
+     * @param Closure|null $callback       The callback to execute after the test.
+     */
+    public function __construct(Deactivatable $deactivatable, Closure $callback = null)
+    {
+        $this->deactivatable = $deactivatable;
+        $this->callback = $callback;
+    }
+    
+    /**
+     * Disables the function mocks.
+     *
+     * @param Test $test The test.
+     * @param int  $time The test duration.
+     *
+     * @see Mock::disable()
+     */
+    public function endTest(Test $test, float $time) : void
+    {
+        parent::endTest($test, $time);
+        
+        $this->deactivatable->disable();
+        if ($this->callback !== null) {
+            ($this->callback)($this);
+        }
+    }
+}
diff --git a/classes/MockObjectProxy.php b/classes/MockObjectProxyNoReturnTypes.php
similarity index 72%
rename from classes/MockObjectProxy.php
rename to classes/MockObjectProxyNoReturnTypes.php
index 4776822..f6e186e 100644
--- a/classes/MockObjectProxy.php
+++ b/classes/MockObjectProxyNoReturnTypes.php
@@ -2,7 +2,8 @@
 
 namespace phpmock\phpunit;
 
-use PHPUnit_Framework_MockObject_MockObject as MockObject;
+use PHPUnit\Framework\MockObject\Matcher\Invocation;
+use PHPUnit\Framework\MockObject\MockObject;
 use phpmock\integration\MockDelegateFunctionBuilder;
 
 /**
@@ -13,9 +14,8 @@
  * @license http://www.wtfpl.net/txt/copying/ WTFPL
  * @internal
  */
-class MockObjectProxy implements MockObject
+class MockObjectProxyNoReturnTypes implements MockObject
 {
-    
     /**
      * @var MockObject $mockObject The mock object.
      */
@@ -55,13 +55,13 @@ public function __phpunit_setOriginalObject($originalObject)
      * @SuppressWarnings(PHPMD)
      */
     // @codingStandardsIgnoreStart
-    public function __phpunit_verify()
+    public function __phpunit_verify(bool $unsetInvocationMocker = true)
     {
         // @codingStandardsIgnoreEnd
-        return $this->mockObject->__phpunit_verify();
+        return $this->mockObject->__phpunit_verify($unsetInvocationMocker);
     }
 
-    public function expects(\PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
+    public function expects(Invocation $matcher)
     {
         return $this->mockObject->expects($matcher)->method(MockDelegateFunctionBuilder::METHOD);
     }
@@ -78,4 +78,14 @@ public function __phpunit_hasMatchers()
         // @codingStandardsIgnoreEnd
         return $this->mockObject->__phpunit_hasMatchers();
     }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration)
+    {
+        // @codingStandardsIgnoreEnd
+        return $this->mockObject->__phpunit_setReturnValueGeneration($returnValueGeneration);
+    }
 }
diff --git a/classes/MockObjectProxyReturnTypes.php b/classes/MockObjectProxyReturnTypes.php
new file mode 100644
index 0000000..77a46a1
--- /dev/null
+++ b/classes/MockObjectProxyReturnTypes.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker;
+use PHPUnit\Framework\MockObject\InvocationMocker;
+use PHPUnit\Framework\MockObject\Matcher\Invocation;
+use PHPUnit\Framework\MockObject\MockObject;
+use phpmock\integration\MockDelegateFunctionBuilder;
+
+/**
+ * Proxy for PHPUnit's PHPUnit_Framework_MockObject_MockObject.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class MockObjectProxyReturnTypes implements MockObject
+{
+    /**
+     * @var MockObject $mockObject The mock object.
+     */
+    private $mockObject;
+
+    /**
+     * Inject the subject.
+     *
+     * @param MockObject $mockObject   The subject.
+     */
+    public function __construct(MockObject $mockObject)
+    {
+        $this->mockObject = $mockObject;
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_getInvocationMocker() : InvocationMocker
+    {
+        // @codingStandardsIgnoreEnd
+        return $this->mockObject->__phpunit_getInvocationMocker();
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_setOriginalObject($originalObject) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_setOriginalObject($originalObject);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_verify(bool $unsetInvocationMocker = true) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_verify($unsetInvocationMocker);
+    }
+
+    public function expects(Invocation $matcher) : BuilderInvocationMocker
+    {
+        return $this->mockObject->expects($matcher)->method(MockDelegateFunctionBuilder::METHOD);
+    }
+    
+    /**
+     * This method is not part of the contract but was found in
+     * PHPUnit's mocked_class.tpl.dist.
+     *
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_hasMatchers() : bool
+    {
+        // @codingStandardsIgnoreEnd
+        return $this->mockObject->__phpunit_hasMatchers();
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_setReturnValueGeneration($returnValueGeneration);
+    }
+}
diff --git a/classes/MockObjectProxyReturnTypes100.php b/classes/MockObjectProxyReturnTypes100.php
new file mode 100644
index 0000000..d6e8977
--- /dev/null
+++ b/classes/MockObjectProxyReturnTypes100.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker;
+use PHPUnit\Framework\MockObject\InvocationHandler;
+use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
+use PHPUnit\Framework\MockObject\MockObject;
+use phpmock\integration\MockDelegateFunctionBuilder;
+
+/**
+ * Proxy for PHPUnit's PHPUnit_Framework_MockObject_MockObject.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class MockObjectProxyReturnTypes100 implements MockObject
+{
+    /**
+     * @var MockObject $mockObject The mock object.
+     */
+    private $mockObject;
+
+    /**
+     * Inject the subject.
+     *
+     * @param MockObject $mockObject   The subject.
+     */
+    public function __construct(MockObject $mockObject)
+    {
+        $this->mockObject = $mockObject;
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_getInvocationHandler(): InvocationHandler
+    {
+        return $this->mockObject->__phpunit_getInvocationHandler();
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_setOriginalObject(object $originalObject) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_setOriginalObject($originalObject);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_verify(bool $unsetInvocationMocker = true) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_verify($unsetInvocationMocker);
+    }
+
+    public function expects(InvocationOrder $matcher) : BuilderInvocationMocker
+    {
+        return $this->mockObject->expects($matcher)->method(MockDelegateFunctionBuilder::METHOD);
+    }
+
+    /**
+     * This method is not part of the contract but was found in
+     * PHPUnit's mocked_class.tpl.dist.
+     *
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_hasMatchers() : bool
+    {
+        // @codingStandardsIgnoreEnd
+        return $this->mockObject->__phpunit_hasMatchers();
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_setReturnValueGeneration($returnValueGeneration);
+    }
+}
diff --git a/classes/MockObjectProxyReturnTypes120.php b/classes/MockObjectProxyReturnTypes120.php
new file mode 100644
index 0000000..8cb79b8
--- /dev/null
+++ b/classes/MockObjectProxyReturnTypes120.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use phpmock\integration\MockDelegateFunctionBuilder;
+use PHPUnit\Framework\Constraint\Constraint;
+use PHPUnit\Framework\MockObject\InvocationStubberImplementation as BuilderInvocationMocker;
+use PHPUnit\Framework\MockObject\InvocationStubber;
+use PHPUnit\Framework\MockObject\InvocationHandler;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
+use PHPUnit\Framework\MockObject\Runtime\PropertyHook;
+
+/**
+ * Proxy for PHPUnit's PHPUnit_Framework_MockObject_MockObject.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class MockObjectProxyReturnTypes120 implements MockObject
+{
+    /**
+     * @var MockObject $mockObject The mock object.
+     */
+    private $mockObject;
+
+    /**
+     * Inject the subject.
+     *
+     * @param MockObject $mockObject   The subject.
+     */
+    public function __construct(MockObject $mockObject)
+    {
+        $this->mockObject = $mockObject;
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_getInvocationHandler(): InvocationHandler
+    {
+        return $this->mockObject->__phpunit_getInvocationHandler();
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_setOriginalObject(object $originalObject) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_setOriginalObject($originalObject);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_verify(bool $unsetInvocationMocker = true) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_verify($unsetInvocationMocker);
+    }
+
+    public function expects(InvocationOrder $matcher) : BuilderInvocationMocker
+    {
+        return $this->mockObject->expects($matcher)->method(MockDelegateFunctionBuilder::METHOD);
+    }
+
+    public function method(Constraint|PropertyHook|string $constraint): InvocationStubber
+    {
+        return $this->mockObject->method($constraint);
+    }
+
+    /**
+     * This method is not part of the contract but was found in
+     * PHPUnit's mocked_class.tpl.dist.
+     *
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_hasMatchers() : bool
+    {
+        // @codingStandardsIgnoreEnd
+        return $this->mockObject->__phpunit_hasMatchers();
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_setReturnValueGeneration($returnValueGeneration);
+    }
+}
diff --git a/classes/MockObjectProxyReturnTypes84.php b/classes/MockObjectProxyReturnTypes84.php
new file mode 100644
index 0000000..8c17e47
--- /dev/null
+++ b/classes/MockObjectProxyReturnTypes84.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace phpmock\phpunit;
+
+use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker;
+use PHPUnit\Framework\MockObject\InvocationHandler;
+use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
+use PHPUnit\Framework\MockObject\MockObject;
+use phpmock\integration\MockDelegateFunctionBuilder;
+
+/**
+ * Proxy for PHPUnit's PHPUnit_Framework_MockObject_MockObject.
+ *
+ * @author Markus Malkusch <markus@malkusch.de>
+ * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
+ * @license http://www.wtfpl.net/txt/copying/ WTFPL
+ * @internal
+ */
+class MockObjectProxyReturnTypes84 implements MockObject
+{
+    /**
+     * @var MockObject $mockObject The mock object.
+     */
+    private $mockObject;
+
+    /**
+     * Inject the subject.
+     *
+     * @param MockObject $mockObject   The subject.
+     */
+    public function __construct(MockObject $mockObject)
+    {
+        $this->mockObject = $mockObject;
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_getInvocationHandler(): InvocationHandler
+    {
+        return $this->mockObject->__phpunit_getInvocationHandler();
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_setOriginalObject($originalObject) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_setOriginalObject($originalObject);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_verify(bool $unsetInvocationMocker = true) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_verify($unsetInvocationMocker);
+    }
+
+    public function expects(InvocationOrder $matcher) : BuilderInvocationMocker
+    {
+        return $this->mockObject->expects($matcher)->method(MockDelegateFunctionBuilder::METHOD);
+    }
+
+    /**
+     * This method is not part of the contract but was found in
+     * PHPUnit's mocked_class.tpl.dist.
+     *
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_hasMatchers() : bool
+    {
+        // @codingStandardsIgnoreEnd
+        return $this->mockObject->__phpunit_hasMatchers();
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD)
+     */
+    // @codingStandardsIgnoreStart
+    public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void
+    {
+        // @codingStandardsIgnoreEnd
+        $this->mockObject->__phpunit_setReturnValueGeneration($returnValueGeneration);
+    }
+}
diff --git a/classes/PHPMock.php b/classes/PHPMock.php
index 0bfcee1..c75a9d3 100644
--- a/classes/PHPMock.php
+++ b/classes/PHPMock.php
@@ -2,25 +2,31 @@
 
 namespace phpmock\phpunit;
 
+use DirectoryIterator;
 use phpmock\integration\MockDelegateFunctionBuilder;
 use phpmock\MockBuilder;
 use phpmock\Deactivatable;
+use PHPUnit\Event\Facade;
+use PHPUnit\Event\Test\Finished;
+use PHPUnit\Framework\MockObject\MockObject;
+use ReflectionClass;
+use ReflectionMethod;
+use ReflectionProperty;
+use SebastianBergmann\Template\Template;
 
 /**
- * Adds building a function mock functionality into PHPUnit_Framework_TestCase.
+ * Adds building a function mock functionality into \PHPUnit\Framework\TestCase.
  *
- * Use this trait in your PHPUnit_Framework_TestCase:
+ * Use this trait in your \PHPUnit\Framework\TestCase:
  * <code>
  * <?php
  *
  * namespace foo;
  *
- * use phpmock\phpunit\PHPMock;
- *
- * class FooTest extends \PHPUnit_Framework_TestCase
+ * class FooTest extends \PHPUnit\Framework\TestCase
  * {
  *
- *     use PHPMock;
+ *     use \phpmock\phpunit\PHPMock;
  *
  *     public function testBar()
  *     {
@@ -37,29 +43,12 @@
  */
 trait PHPMock
 {
+    public static $templatesPath = '/tmp';
 
-    /**
-     * Returns a builder object to create mock objects using a fluent interface.
-     *
-     * This method exists in PHPUnit_Framework_TestCase.
-     *
-     * @param string $className Name of the class to mock.
-     * @return \PHPUnit_Framework_MockObject_MockBuilder
-     * @see \PHPUnit_Framework_TestCase::getMockBuilder()
-     * @internal
-     */
-    abstract protected function getMockBuilder($className);
-
-    /**
-     * Returns the test result.
-     *
-     * This method exists in PHPUnit_Framework_TestCase.
-     *
-     * @return \PHPUnit_Framework_TestResult The test result.
-     * @see \PHPUnit_Framework_TestCase::getTestResultObject()
-     * @internal
-     */
-    abstract protected function getTestResultObject();
+    private $phpunitVersionClass = '\\PHPUnit\\Runner\\Version';
+    private $openInvocation = 'new \\PHPUnit\\Framework\\MockObject\\Invocation(';
+    private $openWrapper = '\\phpmock\\phpunit\\DefaultArgumentRemover::removeDefaultArgumentsWithReflection(';
+    private $closeFunc = ')';
 
     /**
      * Returns the enabled function mock.
@@ -69,31 +58,47 @@ abstract protected function getTestResultObject();
      * @param string $namespace The function namespace.
      * @param string $name      The function name.
      *
-     * @return \PHPUnit_Framework_MockObject_MockObject The PHPUnit mock.
+     * @return MockObject The PHPUnit mock.
      */
     public function getFunctionMock($namespace, $name)
     {
+        $this->prepareCustomTemplates();
+
         $delegateBuilder = new MockDelegateFunctionBuilder();
         $delegateBuilder->build($name);
-        
-        $mock = $this->getMockBuilder($delegateBuilder->getFullyQualifiedClassName())->getMockForAbstractClass();
-        
-        $mock->__phpunit_getInvocationMocker()->addMatcher(new DefaultArgumentRemover());
-        
+
+        $builder = $this->getMockBuilder($delegateBuilder->getFullyQualifiedClassName());
+        if (is_callable([$builder, 'onlyMethods'])) {
+            $builder->onlyMethods([MockDelegateFunctionBuilder::METHOD, $name]);
+        } elseif (is_callable([$builder, 'setMethods'])) {
+            $builder->setMethods([MockDelegateFunctionBuilder::METHOD, $name]);
+        }
+        $mock = $builder->getMock();
+        $this->addMatcher($mock, $name);
+
         $functionMockBuilder = new MockBuilder();
         $functionMockBuilder->setNamespace($namespace)
                             ->setName($name)
                             ->setFunctionProvider($mock);
-                
+
         $functionMock = $functionMockBuilder->build();
         $functionMock->enable();
-        
+
         $this->registerForTearDown($functionMock);
-        
-        $proxy = new MockObjectProxy($mock);
-        return $proxy;
+
+        return new MockObjectProxy($mock);
     }
-    
+
+    private function addMatcher($mock, $name)
+    {
+        if (is_callable([$mock, '__phpunit_getInvocationHandler'])) {
+            $mocker = $mock->__phpunit_getInvocationHandler()->expects(new DefaultArgumentRemover());
+            $mocker->method($name);
+            return;
+        }
+        $mock->__phpunit_getInvocationMocker()->addMatcher(new DefaultArgumentRemover());
+    }
+
     /**
      * Automatically disable function mocks after the test was run.
      *
@@ -102,13 +107,57 @@ public function getFunctionMock($namespace, $name)
      * directly created with PHPMock's API.
      *
      * @param Deactivatable $deactivatable The function mocks.
+     *
+     * @SuppressWarnings(PHPMD.StaticAccess)
      */
     public function registerForTearDown(Deactivatable $deactivatable)
     {
+        if (class_exists(Facade::class)) {
+            $facade = Facade::instance();
+
+            $property = new ReflectionProperty(Facade::class, 'sealed');
+            $property->setAccessible(true);
+            $property->setValue($facade, false);
+
+            $method = new ReflectionMethod($facade, 'deferredDispatcher');
+            $method->setAccessible(true);
+            $dispatcher = $method->invoke($facade);
+
+            $propDispatcher = new ReflectionProperty($dispatcher, 'dispatcher');
+            $propDispatcher->setAccessible(true);
+            $directDispatcher = $propDispatcher->getValue($dispatcher);
+
+            $propSubscribers = new ReflectionProperty($directDispatcher, 'subscribers');
+            $propSubscribers->setAccessible(true);
+
+            $facade->registerSubscriber(
+                new MockDisabler(
+                    $deactivatable,
+                    static function (MockDisabler $original) use ($directDispatcher, $propSubscribers) {
+                        $subscribers = $propSubscribers->getValue($directDispatcher);
+
+                        foreach ($subscribers[Finished::class] as $key => $subscriber) {
+                            if ($original === $subscriber) {
+                                unset($subscribers[Finished::class][$key]);
+                            }
+                        }
+
+                        $propSubscribers->setValue($directDispatcher, $subscribers);
+                    }
+                )
+            );
+
+            $property->setValue($facade, true);
+
+            return;
+        }
+
         $result = $this->getTestResultObject();
-        $result->addListener(new MockDisabler($deactivatable));
+        $result->addListener(new MockDisabler($deactivatable, static function (MockDisabler $listener) use ($result) {
+            $result->removeListener($listener);
+        }));
     }
-    
+
     /**
      * Defines the mocked function in the given namespace.
      *
@@ -137,4 +186,131 @@ public static function defineFunctionMock($namespace, $name)
                             ->build()
                             ->define();
     }
+
+    /**
+     * Adds a wrapper method to the Invocable object instance that makes it
+     * possible to remove optional parameters when it is declared read-only.
+     *
+     * @return void
+     *
+     * @SuppressWarnings(PHPMD.StaticAccess)
+     * @SuppressWarnings(PHPMD.IfStatementAssignment)
+     */
+    private function prepareCustomTemplates()
+    {
+        if (!($this->shouldPrepareCustomTemplates() &&
+            is_dir(static::$templatesPath) &&
+            ($phpunitTemplatesDir = $this->getPhpunitTemplatesDir())
+        )) {
+            return;
+        }
+
+        $templatesDir = realpath(static::$templatesPath);
+        $directoryIterator = new DirectoryIterator($phpunitTemplatesDir);
+
+        $templates = [];
+
+        $prefix = 'phpmock-phpunit-' . $this->getPhpUnitVersion() . '-';
+
+        foreach ($directoryIterator as $fileinfo) {
+            if ($fileinfo->getExtension() !== 'tpl') {
+                continue;
+            }
+
+            $filename = $fileinfo->getFilename();
+            $customTemplateFile = $templatesDir . DIRECTORY_SEPARATOR . $prefix . $filename;
+            $templateFile = $phpunitTemplatesDir . DIRECTORY_SEPARATOR . $filename;
+
+            $this->createCustomTemplateFile($templateFile, $customTemplateFile);
+
+            if (file_exists($customTemplateFile)) {
+                $templates[$templateFile] = new Template($customTemplateFile);
+            }
+        }
+
+        $mockMethodClasses = [
+            'PHPUnit\\Framework\\MockObject\\Generator\\DoubledMethod', // PHPUnit 12
+            'PHPUnit\\Framework\\MockObject\\Generator\\MockMethod',    // PHPUnit 10-11
+            'PHPUnit\\Framework\\MockObject\\MockMethod',               // PHPUnit 9
+        ];
+
+        foreach ($mockMethodClasses as $mockMethodClass) {
+            if (class_exists($mockMethodClass)) {
+                $reflection = new ReflectionClass($mockMethodClass);
+
+                $reflectionTemplates = $reflection->getProperty('templates');
+                $reflectionTemplates->setAccessible(true);
+
+                $reflectionTemplates->setValue(null, $templates);
+
+                break;
+            }
+        }
+    }
+
+    private function shouldPrepareCustomTemplates()
+    {
+        return class_exists($this->phpunitVersionClass)
+            && version_compare($this->getPhpUnitVersion(), '10.0.0') >= 0;
+    }
+
+    private function getPhpUnitVersion()
+    {
+        return call_user_func([$this->phpunitVersionClass, 'id']);
+    }
+
+    /**
+     * Detects the PHPUnit templates dir
+     *
+     * @return string|null
+     */
+    private function getPhpunitTemplatesDir()
+    {
+        $phpunitLocations = [
+            __DIR__ . '/../../',
+            __DIR__ . '/../vendor/',
+        ];
+
+        $phpunitRelativePath = '/phpunit/phpunit/src/Framework/MockObject/Generator';
+
+        foreach ($phpunitLocations as $prefix) {
+            $possibleDirs = [
+                $prefix . $phpunitRelativePath . '/templates',
+                $prefix . $phpunitRelativePath,
+            ];
+
+            foreach ($possibleDirs as $dir) {
+                if (is_dir($dir)) {
+                    return realpath($dir);
+                }
+            }
+        }
+    }
+
+    /**
+     * Clones original template with the wrapper
+     *
+     * @param string $templateFile Template filename
+     * @param string $customTemplateFile Custom template filename
+     *
+     * @return void
+     *
+     * @SuppressWarnings(PHPMD.IfStatementAssignment)
+     */
+    private function createCustomTemplateFile(string $templateFile, string $customTemplateFile)
+    {
+        $template = file_get_contents($templateFile);
+
+        if (($start = strpos($template, $this->openInvocation)) !== false &&
+            ($end = strpos($template, $this->closeFunc, $start)) !== false
+        ) {
+            $template = substr_replace($template, $this->closeFunc, $end, 0);
+            $template = substr_replace($template, $this->openWrapper, $start, 0);
+
+            if ($file = fopen($customTemplateFile, 'w+')) {
+                fputs($file, $template);
+                fclose($file);
+            }
+        }
+    }
 }
diff --git a/compatibility/BaseTestListener.php b/compatibility/BaseTestListener.php
new file mode 100644
index 0000000..1ddd1a9
--- /dev/null
+++ b/compatibility/BaseTestListener.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PHPUnit\Framework;
+
+/**
+ * Compatibility class to work with PHPUnit 7
+ *
+ * @internal
+ */
+abstract class BaseTestListener implements TestListener
+{
+    use TestListenerDefaultImplementation;
+}
diff --git a/composer.json b/composer.json
index 9a1aa20..bbd0703 100644
--- a/composer.json
+++ b/composer.json
@@ -2,7 +2,7 @@
     "name": "php-mock/php-mock-phpunit",
     "type": "library",
     "description": "Mock built-in PHP functions (e.g. time()) with PHPUnit. This package relies on PHP's namespace fallback policy. No further extension is needed.",
-    "keywords": ["phpunit", "mock", "stub", "test double", "function", "test", "TDD", "BDD"],
+    "keywords": ["phpunit", "mock", "stub", "test double", "function", "test", "testing", "TDD", "BDD"],
     "homepage": "https://github.com/php-mock/php-mock-phpunit",
     "license": "WTFPL",
     "authors": [
@@ -14,17 +14,18 @@
         }
     ],
     "autoload": {
+        "files": ["autoload.php"],
         "psr-4": {"phpmock\\phpunit\\": "classes/"}
     },
     "require": {
-        "php": ">=5.5",
-        "phpunit/phpunit": "^4.0.0 || ^5.0.0",
-        "php-mock/php-mock-integration": "^1"
+        "php": ">=7",
+        "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17 || ^11 || ^12.0.9",
+        "php-mock/php-mock-integration": "^3.0"
+    },
+    "require-dev": {
+        "mockery/mockery": "^1.3.6"
     },
     "archive": {
         "exclude": ["/tests"]
-    },
-    "conflict": {
-        "phpunit/phpunit-mock-objects": "3.2.0"
     }
 }
diff --git a/tests/MockDisablerTest.php b/tests/MockDisablerTest.php
index 03f6ccb..4dbadca 100644
--- a/tests/MockDisablerTest.php
+++ b/tests/MockDisablerTest.php
@@ -2,7 +2,9 @@
 
 namespace phpmock\phpunit;
 
+use phpmock\Deactivatable;
 use phpmock\Mock;
+use PHPUnit\Framework\TestCase;
 
 /**
  * Tests MockDisabler.
@@ -12,7 +14,7 @@
  * @license http://www.wtfpl.net/txt/copying/ WTFPL
  * @see MockDisabler
  */
-class MockDisablerTest extends \PHPUnit_Framework_TestCase
+class MockDisablerTest extends TestCase
 {
 
     /**
@@ -31,4 +33,22 @@ public function testEndTest()
         
         $this->assertEquals(1, min(1, 9));
     }
+
+    public function testCallback()
+    {
+        $executed = false;
+        $executedWith = null;
+        $mock = $this->createMock(Deactivatable::class);
+        $disabler = new MockDisabler($mock, static function ($disabler) use (&$executed, &$executedWith) {
+            self::assertInstanceOf(MockDisabler::class, $disabler);
+
+            $executed = true;
+            $executedWith = $disabler;
+        });
+
+        $disabler->endTest($this, 1);
+
+        self::assertTrue($executed);
+        self::assertSame($executedWith, $disabler);
+    }
 }
diff --git a/tests/MockObjectProxyTest.php b/tests/MockObjectProxyTest.php
index 0652c05..e9f35e4 100644
--- a/tests/MockObjectProxyTest.php
+++ b/tests/MockObjectProxyTest.php
@@ -2,8 +2,18 @@
 
 namespace phpmock\phpunit;
 
-use \PHPUnit_Framework_MockObject_Builder_InvocationMocker as InvocationMocker;
+use Mockery;
 use phpmock\integration\MockDelegateFunctionBuilder;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\MockObject\Builder\InvocationMocker;
+use PHPUnit\Framework\MockObject\ConfigurableMethod;
+use PHPUnit\Framework\MockObject\InvocationHandler;
+use PHPUnit\Framework\MockObject\Matcher\Invocation;
+use PHPUnit\Framework\MockObject\Matcher\MethodName;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\MockObject\Stub\MatcherCollection;
+use PHPUnit\Framework\TestCase;
+use SebastianBergmann\Type\Type;
 
 /**
  * Tests MockObjectProxyTest.
@@ -14,9 +24,8 @@
  * @see MockObjectProxy
  * @requires PHPUnit 4.5.0
  */
-class MockObjectProxyTest extends \PHPUnit_Framework_TestCase
+class MockObjectProxyTest extends TestCase
 {
-
     /**
      * Tests expects()
      *
@@ -24,20 +33,79 @@ class MockObjectProxyTest extends \PHPUnit_Framework_TestCase
      */
     public function testExpects()
     {
-        $matcher = $this->getMockBuilder(\PHPUnit_Framework_MockObject_Matcher_Invocation::class)->getMock();
+        $matcher = $this->getMockBuilder(Invocation::class)->getMock();
+
+        if (class_exists(ConfigurableMethod::class)) {
+            if (class_exists(\PHPUnit\Runner\Version::class)
+                && version_compare(\PHPUnit\Runner\Version::id(), '10.5.0') >= 0
+            ) {
+                $methods = new ConfigurableMethod(
+                    MockDelegateFunctionBuilder::METHOD,
+                    [],
+                    0,
+                    Mockery::mock(Type::class)
+                );
+            } else {
+                $methods = new ConfigurableMethod(
+                    MockDelegateFunctionBuilder::METHOD,
+                    Mockery::mock(Type::class)
+                );
+            }
+        } else {
+            $methods = [MockDelegateFunctionBuilder::METHOD];
+        }
 
-        $invocationMocker = $this->getMockBuilder(InvocationMocker::class)->disableOriginalConstructor()->getMock();
-        $invocationMocker->expects($this->once())->method("method")
-                ->with(MockDelegateFunctionBuilder::METHOD)->willReturn($invocationMocker);
+        if (class_exists(\PHPUnit\Runner\Version::class)
+            && version_compare(\PHPUnit\Runner\Version::id(), '8.4.0') >= 0
+        ) {
+            $invocationHandler = new InvocationHandler([$methods], false);
+            $invocationMocker = $invocationHandler->expects($matcher);
+        } else {
+            $invocationMocker = new InvocationMocker(
+                $this->getMockBuilder(MatcherCollection::class)->getMock(),
+                $this->getMockBuilder(Invocation::class)->getMock(),
+                $methods
+            );
+        }
 
-        $prophecy = $this->prophesize(\PHPUnit_Framework_MockObject_MockObject::class);
-        $prophecy->expects($matcher)->willReturn($invocationMocker);
-        $mock = $prophecy->reveal();
+        $mock = Mockery::mock(MockObject::class);
+        $mock->shouldReceive('expects')->with($matcher)->andReturn($invocationMocker);
 
         $proxy = new MockObjectProxy($mock);
 
         $result = $proxy->expects($matcher);
         $this->assertEquals($invocationMocker, $result);
+
+        $this->assertSame(
+            (new MethodName(MockDelegateFunctionBuilder::METHOD))->toString(),
+            $this->getMethodMatcher($invocationMocker)->toString()
+        );
+    }
+
+    private function getMethodMatcher($invocationMocker)
+    {
+        $hasVersion = class_exists(\PHPUnit\Runner\Version::class);
+
+        if ($hasVersion
+            && version_compare(\PHPUnit\Runner\Version::id(), '10.0.0') >= 0
+        ) {
+            $reflection = new \ReflectionClass(InvocationMocker::class);
+            $property = $reflection->getProperty('matcher');
+            $property->setAccessible(true);
+            return $property->getValue($invocationMocker)->methodNameRule();
+        }
+
+        if ($hasVersion
+            && version_compare(\PHPUnit\Runner\Version::id(), '8.4.0') >= 0
+        ) {
+            $reflection = new \ReflectionClass(InvocationMocker::class);
+            $property = $reflection->getProperty('matcher');
+            $property->setAccessible(true);
+            return $property->getValue($invocationMocker)->getMethodNameRule();
+        }
+
+        return $invocationMocker->getMatcher()->methodNameMatcher ??
+            $invocationMocker->getMatcher()->getMethodNameMatcher();
     }
 
     /**
@@ -52,14 +120,13 @@ public function testExpects()
      */
     public function testHasMatcher()
     {
-        $prophecy = $this->prophesize(\PHPUnit_Framework_MockObject_MockObject::class);
-        $prophecy->__phpunit_hasMatchers()->willReturn("foo");
-        $mock = $prophecy->reveal();
+        $mock = Mockery::mock(MockObject::class);
+        $mock->shouldReceive('__phpunit_hasMatchers')->andReturn(true);
 
         $proxy = new MockObjectProxy($mock);
 
         $result = $proxy->__phpunit_hasMatchers();
-        $this->assertEquals("foo", $result);
+        $this->assertTrue($result);
     }
 
     /**
@@ -71,29 +138,49 @@ public function testHasMatcher()
      * @test
      * @dataProvider provideTestProxiedMethods
      */
-    public function testProxiedMethods($method, array $arguments = [], $expected = "foo")
+    #[DataProvider('provideTestProxiedMethods')]
+    public function testProxiedMethods($method, array $arguments = [], $expected = null)
     {
-        $prophecy = $this->prophesize(\PHPUnit_Framework_MockObject_MockObject::class);
-        call_user_func_array([$prophecy, $method], $arguments)->willReturn($expected);
-        $mock = $prophecy->reveal();
+        $mock = Mockery::mock(MockObject::class);
+        if ($expected) {
+            $mock->shouldReceive($method)->withArgs($arguments)->andReturn($expected)->times(1);
+        } else {
+            $mock->shouldReceive($method)->withArgs($arguments)->times(1);
+        }
 
         $proxy = new MockObjectProxy($mock);
 
         $result = call_user_func_array([$proxy, $method], $arguments);
-        $this->assertEquals($expected, $result);
+
+        if ($expected) {
+            $this->assertSame($expected, $result);
+        }
     }
 
     /**
      * Returns the test cases for testProxiedMethods().
      *
      * @return array Test cases.
+     *
+     * @SuppressWarnings(PHPMD)
      */
-    public function provideTestProxiedMethods()
+    public static function provideTestProxiedMethods()
     {
-        return [
-            ["__phpunit_getInvocationMocker"],
-            ["__phpunit_setOriginalObject", ["bar"]],
-            ["__phpunit_verify"],
-        ];
+        $return = [];
+        if (class_exists(\PHPUnit\Runner\Version::class)
+            && version_compare(\PHPUnit\Runner\Version::id(), '8.4.0') >= 0
+        ) {
+            $return[] = ['__phpunit_getInvocationHandler', [], new InvocationHandler([], false)];
+        } else {
+            $return[] = [
+                '__phpunit_getInvocationMocker',
+                [],
+                new \PHPUnit\Framework\MockObject\InvocationMocker([], true)
+            ];
+        }
+
+        $return[] = ['__phpunit_setOriginalObject', [new \stdClass()]];
+        $return[] = ['__phpunit_verify', [true]];
+        return $return;
     }
 }
diff --git a/tests/PHPMockTest.php b/tests/PHPMockTest.php
index dbd2d5a..582b1c7 100644
--- a/tests/PHPMockTest.php
+++ b/tests/PHPMockTest.php
@@ -2,7 +2,9 @@
 
 namespace phpmock\phpunit;
 
-use phpmock\AbstractMockTest;
+use phpmock\AbstractMockTestCase;
+use phpmock\Deactivatable;
+use PHPUnit\Framework\ExpectationFailedException;
 
 /**
  * Tests PHPMock.
@@ -12,26 +14,26 @@
  * @license http://www.wtfpl.net/txt/copying/ WTFPL
  * @see PHPMock
  */
-class PHPMockTest extends AbstractMockTest
+class PHPMockTest extends AbstractMockTestCase
 {
 
     use PHPMock;
-    
+
     protected function defineFunction($namespace, $functionName)
     {
         self::defineFunctionMock($namespace, $functionName);
     }
-    
+
     protected function mockFunction($namespace, $functionName, callable $function)
     {
         $mock = $this->getFunctionMock($namespace, $functionName);
         $mock->expects($this->any())->willReturnCallback($function);
     }
-    
+
     protected function disableMocks()
     {
     }
-    
+
     /**
      * Tests building a mock with arguments.
      *
@@ -41,10 +43,10 @@ public function testFunctionMockWithArguments()
     {
         $time = $this->getFunctionMock(__NAMESPACE__, "sqrt");
         $time->expects($this->once())->with(9)->willReturn(2);
-        
+
         $this->assertEquals(2, sqrt(9));
     }
-    
+
     /**
      * Tests failing an expectation.
      *
@@ -58,8 +60,67 @@ public function testFunctionMockFailsExpectation()
 
             $time->__phpunit_verify();
             $this->fail("Expectation should fail");
-        } catch (\PHPUnit_Framework_ExpectationFailedException $e) {
+        } catch (ExpectationFailedException $e) {
             time(); // satisfy the expectation
         }
     }
+
+    /**
+     * Register a Deactivatable for a tear down.
+     *
+     * @test
+     */
+    public function testRegisterForTearDownRegistered()
+    {
+        $obj = new \stdClass();
+        $obj->count = 0;
+
+        $class = new class ($obj) implements Deactivatable
+        {
+            private $obj;
+
+            public function __construct($obj)
+            {
+                $this->obj = $obj;
+            }
+
+            public function disable()
+            {
+                ++$this->obj->count;
+            }
+        };
+        $this->registerForTearDown($class);
+
+        self::assertSame(0, $obj->count);
+
+        return $obj;
+    }
+
+    /**
+     * Check the Deactivatable was executed on a tear down of dependent test.
+     *
+     * @test
+     *
+     * @depends testRegisterForTearDownRegistered
+     */
+    #[\PHPUnit\Framework\Attributes\Depends('testRegisterForTearDownRegistered')]
+    public function testRegisterForTearDownExecuted($obj)
+    {
+        self::assertSame(1, $obj->count);
+
+        return $obj;
+    }
+
+    /**
+     * Check the Deactivatable was unregistered after executing, so it is not executed again.
+     *
+     * @test
+     *
+     * @depends testRegisterForTearDownExecuted
+     */
+    #[\PHPUnit\Framework\Attributes\Depends('testRegisterForTearDownExecuted')]
+    public function testRegisterForTearDownRemoved($obj)
+    {
+        self::assertSame(1, $obj->count);
+    }
 }
