diff --git a/scipost_django/mailing_lists/factories.py b/scipost_django/mailing_lists/factories.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b1e60c7cb86deb66df9db814baab4e10d0768ba
--- /dev/null
+++ b/scipost_django/mailing_lists/factories.py
@@ -0,0 +1,36 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+
+import factory
+from django.utils.text import slugify
+
+from .models import *
+
+
+class MailingListFactory(factory.django.DjangoModelFactory):
+    class Meta:
+        model = MailingList
+
+    name = factory.Faker("word")
+    slug = factory.LazyAttribute(lambda self: slugify(self.name))
+
+    is_opt_in = False
+
+    @factory.post_generation
+    def eligible_subscribers(self, create, extracted, **kwargs):
+        if not create:
+            return
+
+        if extracted:
+            for contributor in extracted:
+                self.eligible_subscribers.add(contributor)
+
+    @factory.post_generation
+    def subscribed(self, create, extracted, **kwargs):
+        if not create:
+            return
+
+        if extracted:
+            for contributor in extracted:
+                self.subscribed.add(contributor)
diff --git a/scipost_django/mailing_lists/tests.py b/scipost_django/mailing_lists/tests.py
deleted file mode 100644
index ddef03c4df91383dfce15a034976e3a469d77d70..0000000000000000000000000000000000000000
--- a/scipost_django/mailing_lists/tests.py
+++ /dev/null
@@ -1,7 +0,0 @@
-__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
-__license__ = "AGPL v3"
-
-
-from django.test import TestCase
-
-# Create your tests here.
diff --git a/scipost_django/mailing_lists/tests/__init__.py b/scipost_django/mailing_lists/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/scipost_django/mailing_lists/tests/test_factories.py b/scipost_django/mailing_lists/tests/test_factories.py
new file mode 100644
index 0000000000000000000000000000000000000000..f01b42cc8a6cd828e69ebf325466d38273e4d5d0
--- /dev/null
+++ b/scipost_django/mailing_lists/tests/test_factories.py
@@ -0,0 +1,57 @@
+__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
+__license__ = "AGPL v3"
+
+from django.test import TestCase
+
+from profiles.factories import ProfileEmailFactory
+from scipost.factories import ContributorFactory
+from ..factories import *
+
+
+class TestMailingListFactory(TestCase):
+    def test_can_create_mailing_lists(self):
+        mailing_list = MailingListFactory()
+        self.assertIsNotNone(mailing_list)
+
+    def test_can_subscribe_contributor(self):
+        profile_email = ProfileEmailFactory(primary=True)
+        contributor = ContributorFactory.from_profile(profile_email.profile)
+        mailing_list = MailingListFactory()
+        mailing_list.add_eligible_subscriber(contributor)
+
+        self.assertEqual(mailing_list.eligible_subscribers.count(), 1)
+        self.assertEqual(mailing_list.subscribed.count(), 1)
+        self.assertEquals(mailing_list.email_list, [profile_email.email])
+
+    def test_can_unsubscribe_contributor(self):
+        profile_email = ProfileEmailFactory(primary=True)
+        contributor = ContributorFactory.from_profile(profile_email.profile)
+        mailing_list = MailingListFactory()
+
+        mailing_list.add_eligible_subscriber(contributor)
+        mailing_list.unsubscribe(contributor)
+
+        self.assertEqual(mailing_list.eligible_subscribers.count(), 1)
+        self.assertEqual(mailing_list.subscribed.count(), 0)
+        self.assertEquals(mailing_list.email_list, [])
+
+    def test_does_not_automatically_subscribe_if_opt_in(self):
+        profile_email = ProfileEmailFactory(primary=True)
+        contributor = ContributorFactory.from_profile(profile_email.profile)
+        mailing_list = MailingListFactory(is_opt_in=True)
+        mailing_list.add_eligible_subscriber(contributor)
+
+        self.assertEqual(mailing_list.eligible_subscribers.count(), 1)
+        self.assertEqual(mailing_list.subscribed.count(), 0)
+        self.assertEquals(mailing_list.email_list, [])
+
+    def test_does_not_include_nonprimary_emails(self):
+        profile_email = ProfileEmailFactory(primary=False)
+        profile_email_primary = ProfileEmailFactory(
+            primary=True, profile=profile_email.profile
+        )
+        contributor = ContributorFactory.from_profile(profile_email.profile)
+        mailing_list = MailingListFactory()
+        mailing_list.add_eligible_subscriber(contributor)
+
+        self.assertEquals(mailing_list.email_list, [profile_email_primary.email])