diff --git a/opentech/apply/tests/factories.py b/opentech/apply/tests/factories.py
index ab8339646d1f65b3c82fb8a3ed69e076f5b6671a..171ee82c31809192a0e907d43f466e7ede18c10f 100644
--- a/opentech/apply/tests/factories.py
+++ b/opentech/apply/tests/factories.py
@@ -2,7 +2,7 @@ from django.forms import Form
 import factory
 import wagtail_factories
 
-from opentech.apply.models import FundPage
+from opentech.apply.models import ApplicationForm, FundPage, FundPageForm
 from opentech.apply.workflow import Action, Phase, Stage, Workflow
 
 
@@ -114,3 +114,25 @@ class WorkflowFactory(factory.Factory):
 class FundPageFactory(wagtail_factories.PageFactory):
     class Meta:
         model = FundPage
+
+    class Params:
+        workflow_stages = 1
+        number_forms = 1
+
+    # Will need to update how the stages are identified as Fund Page changes
+    workflow = factory.LazyAttribute(lambda o: list(FundPage.WORKFLOWS.keys())[o.workflow_stages - 1])
+    # forms = factory.SubFactory('opentech.apply.tests.factories.FundPageFormFactory'. fund=
+
+
+class FundPageFormFactory(factory.DjangoModelFactory):
+    class Meta:
+        model = FundPageForm
+    fund = factory.SubFactory(FundPageFactory, parent=None)
+    form = factory.SubFactory('opentech.apply.tests.factories.ApplicationFormFactory')
+
+
+class ApplicationFormFactory(factory.DjangoModelFactory):
+    class Meta:
+        model = ApplicationForm
+
+    name = factory.Faker('word')
diff --git a/opentech/apply/tests/test_admin_form.py b/opentech/apply/tests/test_admin_form.py
new file mode 100644
index 0000000000000000000000000000000000000000..4cef0219594359dd152c3adcd9d5a201e687a6f6
--- /dev/null
+++ b/opentech/apply/tests/test_admin_form.py
@@ -0,0 +1,63 @@
+from django.test import TestCase
+
+import factory
+
+from opentech.apply.models import FundPage
+
+from .factories import ApplicationFormFactory, FundPageFactory
+
+
+def formset_base(field, total, delete):
+    base_data = {
+        f'{field}-TOTAL_FORMS': total + delete,
+        f'{field}-INITIAL_FORMS': 0,
+    }
+    application_forms = ApplicationFormFactory.create_batch(total + delete)
+
+    deleted = 0
+    for i, form in enumerate(application_forms):
+        should_delete = deleted < delete
+        base_data.update({
+            f'{field}-{i}-form': form.id,
+            f'{field}-{i}-ORDER': i,
+            f'{field}-{i}-DELETE': should_delete,
+        })
+        deleted += 1
+
+    return base_data
+
+
+def form_data(number_forms=0, delete=0):
+    base_data = formset_base('forms', number_forms, delete)
+    base_data.update(factory.build(dict, FACTORY_CLASS=FundPageFactory))
+    return base_data
+
+
+class TestWorkflowFormAdminForm(TestCase):
+
+    def submit_data(self, data):
+        form_class = FundPage.get_edit_handler().get_form_class(FundPage)
+        return form_class(data=data)
+
+    def test_doesnt_validates_with_no_form(self):
+        form = self.submit_data(form_data())
+        self.assertFalse(form.is_valid())
+        self.assertTrue(form.errors['__all__'])
+
+    def test_validates_with_one_form_one_stage(self):
+        form = self.submit_data(form_data(1))
+        self.assertTrue(form.is_valid(), form.errors.as_text())
+
+    def test_validates_with_one_form_one_stage_with_deleted(self):
+        form = self.submit_data(form_data(1, delete=1))
+        self.assertTrue(form.is_valid(), form.errors.as_text())
+
+    def test_doesnt_validates_with_two_forms_one_stage(self):
+        form = self.submit_data(form_data(2))
+        self.assertFalse(form.is_valid())
+        self.assertTrue(form.errors['__all__'])
+        formset_errors = form.formsets['forms'].errors
+        # First form is ok
+        self.assertFalse(formset_errors[0])
+        # second form is too many
+        self.assertTrue(formset_errors[1]['form'])