diff --git a/.travis.yml b/.travis.yml
index f04b416a..41da5da1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -32,6 +32,12 @@ matrix:
       env: TOXENV=py36-django21
     - python: 3.7
       env: TOXENV=py37-django21
+    - python: 3.5
+      env: TOXENV=py35-django22
+    - python: 3.6
+      env: TOXENV=py36-django22
+    - python: 3.7
+      env: TOXENV=py37-django22
 install:
   - pip install tox
 script:
diff --git a/password_reset/tests/migrations/0001_initial.py b/password_reset/tests/migrations/0001_initial.py
new file mode 100644
index 00000000..21c65c6d
--- /dev/null
+++ b/password_reset/tests/migrations/0001_initial.py
@@ -0,0 +1,65 @@
+# Generated by Django 3.0.dev20190125164321 on 2019-01-25 21:49
+
+import django.contrib.auth.models
+import django.contrib.auth.validators
+from django.db import migrations, models
+import django.db.models.manager
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('auth', '0011_update_proxy_permissions'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='CustomUser',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('password', models.CharField(max_length=128, verbose_name='password')),
+                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+                ('email', models.EmailField(max_length=255, unique=True, verbose_name='email address')),
+                ('is_active', models.BooleanField(default=True)),
+                ('is_admin', models.BooleanField(default=False)),
+                ('date_of_birth', models.DateField()),
+            ],
+            options={
+                'abstract': False,
+            },
+            managers=[
+                ('custom_objects', django.db.models.manager.Manager()),
+            ],
+        ),
+        migrations.CreateModel(
+            name='ExtensionUser',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('password', models.CharField(max_length=128, verbose_name='password')),
+                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+                ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+                ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
+                ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
+                ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
+                ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
+                ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
+                ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
+                ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
+                ('date_of_birth', models.DateField()),
+                ('groups', models.ManyToManyField(blank=True, to='auth.Group')),
+                ('user_permissions', models.ManyToManyField(blank=True, to='auth.Permission')),
+            ],
+            options={
+                'verbose_name': 'user',
+                'verbose_name_plural': 'users',
+                'abstract': False,
+            },
+            managers=[
+                ('custom_objects', django.contrib.auth.models.UserManager()),
+                ('objects', django.contrib.auth.models.UserManager()),
+            ],
+        ),
+    ]
diff --git a/password_reset/tests/migrations/__init__.py b/password_reset/tests/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/setup.py b/setup.py
index db151928..362bb9a6 100644
--- a/setup.py
+++ b/setup.py
@@ -28,6 +28,7 @@
         'Framework :: Django :: 1.11',
         'Framework :: Django :: 2.0',
         'Framework :: Django :: 2.1',
+        'Framework :: Django :: 2.2',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: BSD License',
         'Natural Language :: English',
diff --git a/tox.ini b/tox.ini
index 16d9e15e..00a69f02 100644
--- a/tox.ini
+++ b/tox.ini
@@ -2,9 +2,9 @@
 envlist =
     py27-django111,
     py34-django{111,20},
-    py35-django{111,20,21},
-    py36-django{111,20,21},
-    py37-django{111,20,21},
+    py35-django{111,20,21,22},
+    py36-django{111,20,21,22},
+    py37-django{111,20,21,22},
     docs, lint
 
 [testenv]
@@ -19,6 +19,7 @@ deps =
     django111: Django>=1.11,<2.0
     django20: Django>=2.0,<2.1
     django21: Django>=2.1,<2.2
+    django22: Django>=2.2a1,<3.0
 
 [testenv:docs]
 basepython = python3.7