diff --git a/.github/workflows/test.Dockerfile b/.github/workflows/test.Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..a7acaf1e821c211ad0f0ee45ab06729ebabcccdc
--- /dev/null
+++ b/.github/workflows/test.Dockerfile
@@ -0,0 +1,18 @@
+ARG PHP_VERSION=latest
+FROM php:${PHP_VERSION}-cli-alpine
+
+WORKDIR /workdir
+
+# install composer
+RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
+ENV COMPOSER_ALLOW_SUPERUSER=1
+ENV COMPOSER_HTACCESS_PROTECT=0
+ENV COMPOSER_CACHE_DIR=/.composer
+
+# install PHP extension pcov
+RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \
+    && mkdir -p /usr/src/php/ext/pcov && curl -fsSL https://pecl.php.net/get/pcov | tar xvz -C /usr/src/php/ext/pcov --strip 1 \
+    && docker-php-ext-install pcov \
+    && docker-php-ext-enable pcov \
+    && rm -Rf /usr/src/php/ext/pcov \
+    && apk del --no-cache .build-deps
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cbb4248f5c42b6931261996abdf0f942f8255c43
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,105 @@
+name: Test
+
+on:
+  pull_request:
+  push:
+    branches:
+      - master
+      - '[0-9]+.x'
+
+jobs:
+  php:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        include:
+          - PHP_VERSION: 7.1
+            CODE_COVERAGE: false
+            RUN_PHPSTAN: false
+            RUN_PSALM: false
+            RUN_BENCHMARK: false
+          - PHP_VERSION: 7.2
+            CODE_COVERAGE: true
+            RUN_PHPSTAN: false
+            RUN_PSALM: false
+            RUN_BENCHMARK: false
+          - PHP_VERSION: 7.3
+            CODE_COVERAGE: true
+            RUN_PHPSTAN: false
+            RUN_PSALM: false
+            RUN_BENCHMARK: false
+          - PHP_VERSION: 7.4
+            CODE_COVERAGE: true
+            RUN_PHPSTAN: true
+            RUN_PSALM: true
+            RUN_BENCHMARK: true
+          - PHP_VERSION: 8.0-rc
+            CODE_COVERAGE: true
+            RUN_PHPSTAN: true
+            RUN_PSALM: true
+            RUN_BENCHMARK: true
+            COMPOSER_EXTRA_ARGS: --ignore-platform-reqs
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Cache Docker Image
+        id: cache-docker-image
+        uses: actions/cache@v2
+        with:
+          path: /tmp/docker-image.tar
+          key: cache-docker-image-test:${{ matrix.PHP_VERSION }}
+
+      - name: Load Docker Image
+        if: steps.cache-docker-image.outputs.cache-hit == 'true'
+        run: docker load --input /tmp/docker-image.tar
+
+      - name: Build Docker Image
+        if: steps.cache-docker-image.outputs.cache-hit != 'true'
+        run: docker build -f .github/workflows/test.Dockerfile -t 'test:${{ matrix.PHP_VERSION }}' --build-arg 'PHP_VERSION=${{ matrix.PHP_VERSION }}' .
+
+      - name: Cache Composer Cache Files
+        uses: actions/cache@v2
+        with:
+          path: /tmp/composer-cache-files
+          key: cache-composer-cache-files-${{ matrix.PHP_VERSION }}
+          restore-keys: |
+            cache-composer-cache-files-
+
+      - name: Install Composer Dependencies
+        run: |
+          if [ "${{ matrix.RUN_PHPSTAN }}" != "true" ]; then composer remove --dev phpstan/phpstan --no-update --no-interaction; fi
+          if [ "${{ matrix.RUN_PSALM }}" != "true" ]; then composer remove --dev vimeo/psalm --no-update --no-interaction; fi
+          if [ "${{ matrix.RUN_BENCHMARK }}" != "true" ]; then composer remove --dev phpbench/phpbench --no-update --no-interaction; fi
+          docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd):/workdir" -v '/tmp/composer-cache-files:/.composer' 'test:${{ matrix.PHP_VERSION }}' composer install --no-interaction --no-progress --prefer-dist ${{ matrix.COMPOSER_EXTRA_ARGS }}
+
+      - name: Run Unit Test
+        run: |
+          if [ "${{ matrix.CODE_COVERAGE }}" == "true" ]; then
+            docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd):/workdir" 'test:${{ matrix.PHP_VERSION }}' php -d 'zend.assertions=1' -d 'pcov.enabled=1' ./vendor/bin/phpunit --coverage-clover=.clover.xml
+          else
+            docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd):/workdir" 'test:${{ matrix.PHP_VERSION }}' php -d 'zend.assertions=1' ./vendor/bin/phpunit
+          fi
+
+      - name: Upload Codecov Report
+        uses: codecov/codecov-action@v1
+        if: ${{ matrix.CODE_COVERAGE }}
+        with:
+          token: ${{ secrets.CODECOV_TOKEN }}
+          file: .clover.xml
+
+      - name: Run PHPStan
+        if: ${{ matrix.RUN_PHPSTAN }}
+        run: docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd):/workdir" 'test:${{ matrix.PHP_VERSION }}' php -d 'memory_limit=2G' ./vendor/bin/phpstan analyse --level max src/ tests/
+
+      - name: Run psalm
+        if: ${{ matrix.RUN_PSALM }}
+        run: mkdir -p "$HOME/.cache/psalm" && docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd):/workdir" -v "$HOME/.cache/psalm:/.cache/psalm" 'test:${{ matrix.PHP_VERSION }}' php ./vendor/bin/psalm
+
+      - name: Run benchmark
+        if: ${{ matrix.RUN_BENCHMARK }}
+        run: docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd):/workdir" 'test:${{ matrix.PHP_VERSION }}' php -d 'zend.assertions=-1' ./vendor/bin/phpbench run --no-interaction --revs=1 --retry-threshold=100 --progress=travis
+
+      - name: Export Docker Image
+        if: steps.cache-docker-image.outputs.cache-hit != 'true'
+        run: docker save --output /tmp/docker-image.tar 'test:${{ matrix.PHP_VERSION }}'
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index fb23a5d3ecf1b529d9ccb8a998a4c14ebaec42db..0000000000000000000000000000000000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,83 +0,0 @@
-sudo: false
-language: php
-
-cache:
-  directories:
-    - $HOME/.composer/cache
-    - $HOME/.local
-    - $HOME/ocular.phar
-    - $HOME/phpDocumentor.phar
-
-env:
-  global:
-    - CODE_COVERAGE="0"
-    - PHPDOC="0"
-
-matrix:
-  fast_finish: true
-  include:
-    - php: 7.1
-      env:
-        - CODE_COVERAGE="1"
-        - BENCHMARK="0"
-    - php: 7.2
-      env:
-        - CODE_COVERAGE="1"
-        - BENCHMARK="0"
-    - php: 7.3
-      env:
-        - CODE_COVERAGE="1"
-        - BENCHMARK="1"
-    - php: 7.4
-      env:
-        - CODE_COVERAGE="1"
-        - BENCHMARK="1"
-    - php: nightly
-      env:
-        - CODE_COVERAGE="0"
-        - BENCHMARK="0"
-        - IGNORE_PLATFORM_REQS="1"
-  allow_failures:
-    - php: nightly
-
-install:
-  - if [ "${CODE_COVERAGE}" == "0" ]; then
-      phpenv config-rm xdebug.ini || return 0;
-    fi
-  - if [ "${CODE_COVERAGE}" == "1" ]; then
-      wget -q -N -t 3 --retry-connrefused 'https://scrutinizer-ci.com/ocular.phar' || return 0;
-    fi
-  - if [ "${BENCHMARK}" == "0" ]; then
-      composer remove --dev phpbench/phpbench --no-update;
-    fi
-  - if [ "${IGNORE_PLATFORM_REQS}" == "1" ]; then
-      composer install -n --ignore-platform-reqs;
-    else
-      composer install -n;
-    fi
-
-script:
-  - if [ "$CODE_COVERAGE" == "1" ]; then
-      php -d 'zend.assertions=1' vendor/bin/phpunit --verbose --coverage-text --coverage-clover=coverage.clover;
-    else
-      php -d 'zend.assertions=1' vendor/bin/phpunit --verbose;
-    fi
-
-  # run phpstan
-  - php vendor/bin/phpstan analyse --level max src/ tests/
-
-  # run psalm
-  - php vendor/bin/psalm
-
-  # run benchmarks to make sure they are working fine
-  - if [ "${BENCHMARK}" == "1" ]; then
-      php vendor/bin/phpbench run --no-interaction --revs=1 --retry-threshold=100;
-    fi
-
-after_script:
-  - if [ "${CODE_COVERAGE}" == "1" ]; then
-      php ocular.phar code-coverage:upload --format=php-clover coverage.clover;
-    fi
-
-notifications:
-  email: false
diff --git a/README.md b/README.md
index aa5fe3d5cedba9f5a5e7a20b1435e866e4c50f80..0fa03ac7ae780a5846ac064b1190e618d683fef4 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,11 @@
 # php-enum
-[![Build Status](https://secure.travis-ci.org/marc-mabe/php-enum.png?branch=master)](http://travis-ci.org/marc-mabe/php-enum)
-[![Quality Score](https://scrutinizer-ci.com/g/marc-mabe/php-enum/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/marc-mabe/php-enum/)
-[![Code Coverage](https://scrutinizer-ci.com/g/marc-mabe/php-enum/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/marc-mabe/php-enum/)
-[![Total Downloads](https://poser.pugx.org/marc-mabe/php-enum/downloads.png)](https://packagist.org/packages/marc-mabe/php-enum)
+[![Build Status](https://github.com/marc-mabe/php-enum/workflows/Test/badge.svg?branch=master)](https://github.com/marc-mabe/php-enum/actions?query=workflow%3ATest%20branch%3Amaster)
+[![Code Coverage](https://codecov.io/github/marc-mabe/php-enum/coverage.svg?branch=master)](https://codecov.io/gh/marc-mabe/php-enum/branch/master/)
+[![License](https://poser.pugx.org/marc-mabe/php-enum/license)](https://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt)
 [![Latest Stable](https://poser.pugx.org/marc-mabe/php-enum/v/stable.png)](https://packagist.org/packages/marc-mabe/php-enum)
+[![Total Downloads](https://poser.pugx.org/marc-mabe/php-enum/downloads.png)](https://packagist.org/packages/marc-mabe/php-enum)
+[![Monthly Downloads](https://poser.pugx.org/marc-mabe/php-enum/d/monthly)](https://packagist.org/packages/marc-mabe/php-enum)
+[![Dependents](https://poser.pugx.org/marc-mabe/php-enum/dependents)](https://packagist.org/packages/marc-mabe/php-enum/dependents?order_by=downloads)
 
 This is a native PHP implementation to add enumeration support to PHP.
 It's an abstract class that needs to be extended to use it.
diff --git a/src/Enum.php b/src/Enum.php
index c9de2ea6a1e9d1b47a1b11e5a08d122d6ba7089e..905724a08fca988797ab69ee6fef386c50e4ea58 100644
--- a/src/Enum.php
+++ b/src/Enum.php
@@ -392,6 +392,8 @@ abstract class Enum
             $constants = $scopeConstants + $constants;
         } while (($reflection = $reflection->getParentClass()) && $reflection->name !== __CLASS__);
 
+        /** @var array<string, null|bool|int|float|string|array<mixed>> $constants */
+
         assert(
             self::noAmbiguousValues($constants),
             'Ambiguous enumerator values detected for ' . static::class
diff --git a/tests/MabeEnumTest/EnumMapTest.php b/tests/MabeEnumTest/EnumMapTest.php
index 21e8311e23e81e1debb42aa8bd4f6be43d5f1cec..c390dcf96097c9a1ddfc0db4d468568a975a3802 100644
--- a/tests/MabeEnumTest/EnumMapTest.php
+++ b/tests/MabeEnumTest/EnumMapTest.php
@@ -477,10 +477,13 @@ class EnumMapTest extends TestCase
 
     public function testIsEmpty(): void
     {
-        /** @var array<int, string> $items2 */
-        $items2 = array_combine(Enum32::getValues(), Enum32::getNames());
+        $makeItems2 = function () {
+            foreach (Enum32::getEnumerators() as $enumerator) {
+                yield $enumerator => $enumerator->getName();
+            }
+        };
         $map1 = new EnumMap(Enum32::class, []);
-        $map2 = new EnumMap(Enum32::class, $items2);
+        $map2 = new EnumMap(Enum32::class, $makeItems2());
 
         $this->assertTrue($map1->isEmpty());
         $this->assertFalse($map2->isEmpty());