diff --git a/djblets/configforms/tests/test_config_page.py b/djblets/configforms/tests/test_config_page.py
new file mode 100644
index 0000000000000000000000000000000000000000..b1d098d6486e531acb0390dd4b61681b3844930f
--- /dev/null
+++ b/djblets/configforms/tests/test_config_page.py
@@ -0,0 +1,109 @@
+"""Unit tests for djblets.configforms.pages.ConfigPage."""
+
+from __future__ import unicode_literals
+
+import re
+
+from django.contrib.auth.models import User
+from django.test.client import RequestFactory
+
+from djblets.configforms.forms import ConfigPageForm
+from djblets.configforms.pages import ConfigPage
+from djblets.configforms.views import ConfigPagesView
+from djblets.testing.testcases import TestCase
+
+
+class TestForm1(ConfigPageForm):
+    form_id = 'my-form-1'
+    form_title = 'Form 1'
+
+
+class TestForm2(ConfigPageForm):
+    form_id = 'my-form-2'
+    form_title = 'Form 2'
+
+
+class TestForm3(ConfigPageForm):
+    form_id = 'my-form-3'
+    form_title = 'Form 3'
+
+    def is_visible(self):
+        return False
+
+
+class TestPage(ConfigPage):
+    page_id = 'my-page'
+    form_classes = [TestForm1, TestForm2, TestForm3]
+
+
+class ConfigPageTests(TestCase):
+    """Unit tests for djblets.configforms.pages.ConfigPage."""
+
+    def setUp(self):
+        super(ConfigPageTests, self).setUp()
+
+        self.request = RequestFactory().request()
+        self.user = User.objects.create_user(username='test-user',
+                                             password='test-user')
+        self.page = TestPage(ConfigPagesView, self.request, self.user)
+
+    def test_initial_state(self):
+        """Testing ConfigPage initial state"""
+        self.assertEqual(len(self.page.forms), 2)
+        self.assertIsInstance(self.page.forms[0], TestForm1)
+        self.assertIsInstance(self.page.forms[1], TestForm2)
+
+    def test_is_visible_with_visible_forms(self):
+        """Testing ConfigPage.is_visible with visible forms"""
+        self.assertTrue(self.page.is_visible())
+
+    def test_is_visible_with_no_visible_forms(self):
+        """Testing ConfigPage.is_visible without visible forms"""
+        class TestPage(ConfigPage):
+            page_id = 'my-page'
+            form_classes = [TestForm3]
+
+        page = TestPage(ConfigPagesView, self.request, self.user)
+        self.assertFalse(page.is_visible())
+
+    def test_render(self):
+        """Testing ConfigPage.render"""
+        # Filter out the CSRF token, since it's hard to match.
+        rendered = re.sub(r"<input.+name='csrfmiddlewaretoken'.*>",
+                          '',
+                          self.page.render())
+
+        self.assertHTMLEqual(
+            rendered,
+            '<div class="box-container">'
+            ' <div class="box">'
+            '  <div class="box-inner">'
+            '   <div class="box-head">'
+            '    <h1 class="box-title">Form 1</h1>'
+            '   </div>'
+            '   <div class="box-main box-foot">'
+            '    <form method="post" action=".#my-page" id="form_my-form-1">'
+            '     <input id="id_form_target" name="form_target"'
+            '            type="hidden" value="my-form-1">'
+            '     <input type="submit" class="btn" value="Save">'
+            '    </form>'
+            '   </div>'
+            '  </div>'
+            ' </div>'
+            '</div>'
+            '<div class="box-container">'
+            ' <div class="box">'
+            '  <div class="box-inner">'
+            '   <div class="box-head">'
+            '    <h1 class="box-title">Form 2</h1>'
+            '   </div>'
+            '   <div class="box-main box-foot">'
+            '    <form method="post" action=".#my-page" id="form_my-form-2">'
+            '     <input id="id_form_target" name="form_target"'
+            '            type="hidden" value="my-form-2">'
+            '     <input type="submit" class="btn" value="Save">'
+            '    </form>'
+            '   </div>'
+            '  </div>'
+            ' </div>'
+            '</div>')
diff --git a/djblets/configforms/tests/test_config_page_form.py b/djblets/configforms/tests/test_config_page_form.py
index 2181247a430d8479560b2676470198ebb30352a2..29b942e572bc50bf81c23c605421a372d9c6acbc 100644
--- a/djblets/configforms/tests/test_config_page_form.py
+++ b/djblets/configforms/tests/test_config_page_form.py
@@ -1,3 +1,5 @@
+"""Unit tests for djblets.configforms.forms.ConfigPageForm."""
+
 from __future__ import unicode_literals
 
 import warnings
@@ -39,6 +41,10 @@ class ConfigPageFormTests(TestCase):
 
         self.form = TestForm(page, request, user)
 
+    def test_initial_state(self):
+        """Testing ConfigPageForm initial state"""
+        self.assertEqual(self.form.fields['form_target'].initial, 'my-form')
+
     def test_profile(self):
         """Testing ConfigPageForm.profile raises a deprecation warning"""
         with warnings.catch_warnings(record=True) as w:
@@ -59,3 +65,35 @@ class ConfigPageFormTests(TestCase):
             six.text_type(message),
             'ConfigFormPage.profile is deprecated. Update your code to '
             'fetch the profile manually instead.')
+
+    def test_set_initial(self):
+        """Testing ConfigPageForm.set_initial"""
+        self.form.set_initial({
+            'field1': 'foo',
+            'field2': 'bar',
+        })
+
+        self.assertEqual(self.form.fields['field1'].initial, 'foo')
+        self.assertEqual(self.form.fields['field2'].initial, 'bar')
+
+    def test_render(self):
+        """Testing ConfigPageForm.render"""
+        rendered = self.form.render()
+
+        self.assertHTMLEqual(
+            '<input id="id_form_target" name="form_target"'
+            ' type="hidden" value="my-form">'
+            '<div class="fields-row field-field1" id="row-field1">'
+            ' <div class="field">'
+            '  <label for="id_field1">Field 1:</label>'
+            '  <input id="id_field1" name="field1" type="text">'
+            ' </div>'
+            '</div>'
+            '<div class="fields-row field-field2" id="row-field2">'
+            ' <div class="field">'
+            '  <label for="id_field2">Field 2:</label>'
+            '  <input id="id_field2" name="field2" type="text">'
+            ' </div>'
+            '</div>'
+            '<input type="submit" class="btn" value="Save">',
+            rendered)
diff --git a/djblets/configforms/tests/test_config_page_registry.py b/djblets/configforms/tests/test_config_page_registry.py
index ecd882528490e56389bbd358f3d8310d10b997bc..f5cfcf31b12189c730e30164e7ecdc1c83edcbab 100644
--- a/djblets/configforms/tests/test_config_page_registry.py
+++ b/djblets/configforms/tests/test_config_page_registry.py
@@ -1,3 +1,5 @@
+"""Unit tests for djblets.configforms.registry.ConfigPageRegistry."""
+
 from __future__ import unicode_literals
 
 from kgb import SpyAgency
@@ -33,7 +35,7 @@ class TestPageTwo(DynamicConfigPage):
 
 
 class ConfigPageRegistryTests(SpyAgency, TestCase):
-    """Tests for djblets.configforms.registry.ConfigPageRegistry."""
+    """Unit tests for djblets.configforms.registry.ConfigPageRegistry."""
 
     @classmethod
     def setUpClass(cls):
diff --git a/djblets/configforms/tests/test_config_pages_view.py b/djblets/configforms/tests/test_config_pages_view.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a4d14b10bfdc954773912550db167421fcdc24c
--- /dev/null
+++ b/djblets/configforms/tests/test_config_pages_view.py
@@ -0,0 +1,161 @@
+"""Unit tests for djblets.configforms.views.ConfigPagesView."""
+
+from __future__ import unicode_literals
+
+from django.contrib.auth.models import User
+from django.http import Http404
+from django.test.client import RequestFactory
+from django.utils import six
+
+from djblets.configforms.forms import ConfigPageForm
+from djblets.configforms.pages import ConfigPage
+from djblets.configforms.views import ConfigPagesView
+from djblets.testing.testcases import TestCase
+
+
+class TestForm1(ConfigPageForm):
+    form_id = 'my-form-1'
+    form_title = 'Form 1'
+
+    def save(self):
+        pass
+
+
+class TestForm2(ConfigPageForm):
+    form_id = 'my-form-2'
+    form_title = 'Form 2'
+
+
+class TestForm3(ConfigPageForm):
+    form_id = 'my-form-3'
+    form_title = 'Form 3'
+
+    def is_visible(self):
+        return False
+
+
+class TestPage1(ConfigPage):
+    page_id = 'my-page-1'
+    form_classes = [TestForm1]
+
+
+class TestPage2(ConfigPage):
+    page_id = 'my-page-2'
+    form_classes = [TestForm2]
+
+
+class TestPage3(ConfigPage):
+    page_id = 'my-page-3'
+    form_classes = [TestForm3]
+
+
+class MyConfigPagesView(ConfigPagesView):
+    title = 'My Page Title'
+    nav_title = 'My Nav Entry'
+    page_classes = [TestPage1, TestPage2, TestPage3]
+
+    css_bundle_names = ['my-css-bundle']
+    js_bundle_names = ['my-js-bundle']
+
+    js_model_class = 'MyModel'
+    js_view_class = 'MyView'
+
+    def get_js_model_data(self):
+        return {
+            'my-attr': 'value',
+        }
+
+    def get_js_view_data(self):
+        return {
+            'my-option': 'value',
+        }
+
+
+class ConfigPagesViewTests(TestCase):
+    """Unit tests for djblets.configforms.views.ConfigPagesView."""
+
+    def test_dispatch_initial_state(self):
+        """Testing ConfigPagesView.dispatch initial state"""
+        request = RequestFactory().request()
+        request.user = User.objects.create(username='test-user')
+
+        view = MyConfigPagesView()
+        view.request = request
+
+        response = view.dispatch(view.request)
+        self.assertEqual(response.status_code, 200)
+
+        self.assertEqual(len(view.pages), 3)
+        self.assertIsInstance(view.pages[0], TestPage1)
+        self.assertIsInstance(view.pages[1], TestPage2)
+        self.assertIsInstance(view.pages[2], TestPage3)
+        self.assertEqual(set(six.iterkeys(view.forms)),
+                         {'my-form-1', 'my-form-2'})
+
+    def test_get_context_data(self):
+        """Testing ConfigPagesView.get_context_data"""
+        request = RequestFactory().request()
+        request.user = User.objects.create(username='test-user')
+
+        view = MyConfigPagesView()
+        view.request = request
+
+        view.dispatch(view.request)
+
+        self.assertEqual(
+            view.get_context_data(),
+            {
+                'base_template_name': 'base.html',
+                'page_title': 'My Page Title',
+                'nav_title': 'My Nav Entry',
+                'pages_id': 'config_pages',
+                'pages': view.pages,
+                'css_bundle_names': ['my-css-bundle'],
+                'js_bundle_names': ['my-js-bundle'],
+                'js_model_class': 'MyModel',
+                'js_view_class': 'MyView',
+                'js_model_data': {
+                    'my-attr': 'value',
+                },
+                'js_view_data': {
+                    'my-option': 'value',
+                },
+                'forms': list(six.itervalues(view.forms)),
+            })
+
+    def test_post_without_form_target(self):
+        """Testing ConfigPagesView.dispatch with POST and no form_target"""
+        request = RequestFactory().post('/config/')
+        request.user = User.objects.create(username='test-user')
+        request._dont_enforce_csrf_checks = True
+
+        view = MyConfigPagesView.as_view()
+        response = view(request)
+        self.assertEqual(response.status_code, 400)
+
+    def test_post_with_invalid_form_target(self):
+        """Testing ConfigPagesView.dispatch with POST and invalid form_target
+        """
+        request = RequestFactory().post('/config/', {
+            'form_target': 'bad',
+        })
+        request.user = User.objects.create(username='test-user')
+        request._dont_enforce_csrf_checks = True
+
+        view = MyConfigPagesView.as_view()
+
+        with self.assertRaises(Http404):
+            view(request)
+
+    def test_post_with_success(self):
+        """Testing ConfigPagesView.dispatch with POST and success"""
+        request = RequestFactory().post('/config/', {
+            'form_target': 'my-form-1',
+        })
+        request.user = User.objects.create(username='test-user')
+        request._dont_enforce_csrf_checks = True
+
+        view = MyConfigPagesView.as_view()
+        response = view(request)
+        self.assertEqual(response.status_code, 302)
+        self.assertEqual(response['Location'], '/config/')
diff --git a/djblets/configforms/views.py b/djblets/configforms/views.py
index e3a6efe598ea003c692526033c434909bdc024fe..c6469ba9dcadb6766987fecd7e6d66458ff39180 100644
--- a/djblets/configforms/views.py
+++ b/djblets/configforms/views.py
@@ -105,7 +105,7 @@ class ConfigPagesView(TemplateView):
             return HttpResponseBadRequest()
 
         if form_id not in self.forms:
-            return Http404
+            raise Http404
 
         # Replace the form in the list with a new instantiation containing
         # the form data. If we fail to save, this will ensure the error is
