diff --git a/opentech/public/projects/tests.py b/opentech/public/projects/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..240a8138ac394dad233e9f57cbad37f129b63287
--- /dev/null
+++ b/opentech/public/projects/tests.py
@@ -0,0 +1,82 @@
+import json
+
+from django.test import TestCase
+
+from opentech.apply.categories.models import Option
+from opentech.apply.categories.tests.factories import CategoryFactory, OptionFactory
+
+from .widgets import CategoriesWidget
+
+
+class TestCategoriesWidget(TestCase):
+    def setUp(self):
+        self.category = CategoryFactory()
+        self.options = OptionFactory.create_batch(3, category=self.category)
+
+    def test_init_has_no_queries(self):
+        with self.assertNumQueries(0):
+            CategoriesWidget()
+
+    def test_can_access_categories_and_options(self):
+        widget = CategoriesWidget()
+        widgets = list(widget.widgets)
+        self.assertEqual(len(widgets), 1)
+        choices = list(widgets[0].choices)
+        self.assertEqual(len(choices), len(self.options))
+        self.assertCountEqual(list(choices), list(Option.objects.values_list('id', 'value')))
+
+    def test_can_get_multiple_categories(self):
+        CategoryFactory()
+        widget = CategoriesWidget()
+        widgets = list(widget.widgets)
+        self.assertEqual(len(widgets), 2)
+
+    def test_can_decompress_data(self):
+        widget = CategoriesWidget()
+        value = json.dumps({
+            self.category.id: [self.options[0].id]
+        })
+        self.assertEqual(widget.decompress(value), [[self.options[0].id]])
+
+    def test_can_decompress_multiple_data(self):
+        new_category = CategoryFactory()
+        widget = CategoriesWidget()
+        value = json.dumps({
+            self.category.id: [self.options[0].id],
+            new_category.id: [],
+        })
+        self.assertEqual(widget.decompress(value), [[self.options[0].id], []])
+
+    def test_can_get_data_from_form(self):
+        name = 'categories'
+        widget = CategoriesWidget()
+        submitted_data = {
+            name + '_0': [self.options[1].id],
+        }
+
+        value = widget.value_from_datadict(submitted_data, [], name)
+
+        self.assertEqual(value, json.dumps({self.category.id: [self.options[1].id]}))
+
+    def test_can_get_multiple_data_from_form(self):
+        new_category = CategoryFactory()
+        new_options = OptionFactory.create_batch(3, category=new_category)
+
+        name = 'categories'
+        widget = CategoriesWidget()
+        answer_1 = [self.options[1].id]
+        answer_2 = [new_options[1].id, new_options[2].id]
+        submitted_data = {
+            name + '_0': answer_1,
+            name + '_1': answer_2,
+        }
+
+        value = widget.value_from_datadict(submitted_data, [], name)
+
+        self.assertEqual(
+            value,
+            json.dumps({
+                self.category.id: answer_1,
+                new_category.id: answer_2,
+            })
+        )