diff --git a/scipost_django/colleges/factories.py b/scipost_django/colleges/factories.py
index 6b53f503c3bd7e8cbd1295f60da7592722393fde..bcd957b6f419a6ddfb5d357173c3bc762a998971 100644
--- a/scipost_django/colleges/factories.py
+++ b/scipost_django/colleges/factories.py
@@ -3,18 +3,20 @@ __license__ = "AGPL v3"
 
 
 import factory
-
 from django.utils.text import slugify
-
-from scipost.models import Contributor
+from colleges.models.nomination import FellowshipNomination
+from common.faker import LazyAwareDate
+from ontology.factories import AcademicFieldFactory
+from profiles.factories import ProfileFactory
+from scipost.factories import ContributorFactory
 
 from .models import College, Fellowship
 
 
 class CollegeFactory(factory.django.DjangoModelFactory):
     name = factory.Faker("word")
-    acad_field = factory.SubFactory("ontology.factories.AcademicFieldFactory")
-    slug = factory.LazyAttribute(lambda o: slugify(o.name))
+    acad_field = factory.SubFactory(AcademicFieldFactory)
+    slug = factory.LazyAttribute(lambda self: slugify(self.name))
     order = factory.Sequence(lambda n: College.objects.count() + 1)
 
     class Meta:
@@ -22,12 +24,11 @@ class CollegeFactory(factory.django.DjangoModelFactory):
 
 
 class BaseFellowshipFactory(factory.django.DjangoModelFactory):
-    contributor = factory.Iterator(Contributor.objects.all())
+    college = factory.SubFactory(CollegeFactory)
+    contributor = factory.SubFactory(ContributorFactory)
     start_date = factory.Faker("date_this_year")
     until_date = factory.Faker("date_between", start_date="now", end_date="+2y")
 
-    guest = factory.Faker("boolean", chance_of_getting_true=10)
-
     class Meta:
         model = Fellowship
         django_get_or_create = ("contributor", "start_date")
@@ -35,4 +36,44 @@ class BaseFellowshipFactory(factory.django.DjangoModelFactory):
 
 
 class FellowshipFactory(BaseFellowshipFactory):
-    pass
+    status = "regular"
+
+
+class GuestFellowshipFactory(BaseFellowshipFactory):
+    status = "guest"
+
+
+class SeniorFellowshipFactory(BaseFellowshipFactory):
+    status = "senior"
+
+
+class FellowshipNominationFactory(factory.django.DjangoModelFactory):
+    class Meta:
+        model = FellowshipNomination
+
+    college = factory.SubFactory(CollegeFactory)
+    profile = factory.SubFactory(ProfileFactory)
+    nominated_by = factory.SubFactory(ContributorFactory)
+    nominated_on = LazyAwareDate("date_time_this_year")
+    nominator_comments = factory.Faker("text")
+    fellowship = None
+
+
+class RegisteredFellowshipNominationFactory(FellowshipNominationFactory):
+    @factory.post_generation
+    def create_profile_contributor(self, create, extracted, **kwargs):
+        if not create:
+            return
+        self.profile.contributor = ContributorFactory.from_profile(self.profile)
+        self.profile.save()
+
+
+class SuccessfulFellowshipNominationFactory(RegisteredFellowshipNominationFactory):
+    @factory.post_generation
+    def fellowship(self, create, extracted, **kwargs):
+        if not create:
+            return
+        self.fellowship = FellowshipFactory(
+            contributor=self.profile.contributor, college=self.college
+        )
+        self.fellowship.save()
diff --git a/scipost_django/commentaries/factories.py b/scipost_django/commentaries/factories.py
index e010a1c83ac2a5ac2e7f6dc5116790ebdf52ce07..f93c8c45fc3b93f308f66a44872e9f0c4035f22f 100644
--- a/scipost_django/commentaries/factories.py
+++ b/scipost_django/commentaries/factories.py
@@ -3,6 +3,7 @@ __license__ = "AGPL v3"
 
 
 import factory
+from common.faker import LazyRandEnum
 
 from scipost.constants import SCIPOST_APPROACHES
 from scipost.models import Contributor
@@ -25,14 +26,9 @@ class BaseCommentaryFactory(factory.django.DjangoModelFactory):
     requested_by = factory.SubFactory("scipost.factories.ContributorFactory")
     vetted = True
     vetted_by = factory.SubFactory("scipost.factories.ContributorFactory")
-    type = factory.Iterator(COMMENTARY_TYPES, getter=lambda c: c[0])
+    type = LazyRandEnum(COMMENTARY_TYPES)
     acad_field = factory.SubFactory("ontology.factories.AcademicFieldFactory")
-    approaches = factory.Iterator(
-        SCIPOST_APPROACHES,
-        getter=lambda c: [
-            c[0],
-        ],
-    )
+    approaches = LazyRandEnum(SCIPOST_APPROACHES)
     open_for_commenting = True
 
     title = factory.Faker("sentence")
@@ -63,7 +59,7 @@ class BaseCommentaryFactory(factory.django.DjangoModelFactory):
     @classmethod
     def create(cls, **kwargs):
         if AcademicField.objects.count() < 5:
-            from ontology.factories import AcademicFieldactory
+            from ontology.factories import AcademicFieldFactory
 
             AcademicFieldFactory.create_batch(5)
         if Specialty.objects.count() < 5:
diff --git a/scipost_django/comments/factories.py b/scipost_django/comments/factories.py
index 60dd9c616a51f05b03168767bcd570869143c0cf..7bd42e93934af80b14306b9dd744e2902b1f9de2 100644
--- a/scipost_django/comments/factories.py
+++ b/scipost_django/comments/factories.py
@@ -19,9 +19,8 @@ from faker import Faker
 
 class CommentFactory(factory.django.DjangoModelFactory):
     status = STATUS_VETTED
-    vetted_by = factory.Iterator(Contributor.objects.all())
-
-    author = factory.Iterator(Contributor.objects.all())
+    vetted_by = factory.SubFactory("scipost.factories.ContributorFactory")
+    author = factory.SubFactory("scipost.factories.ContributorFactory")
     comment_text = factory.Faker("paragraph")
     remarks_for_editors = factory.Faker("paragraph")
     file_attachment = Faker().file_name(extension="pdf")
@@ -48,7 +47,7 @@ class CommentaryCommentFactory(CommentFactory):
 
 
 class SubmissionCommentFactory(CommentFactory):
-    content_object = factory.SubFactory("submissions.factories.Submission")
+    content_object = factory.SubFactory("submissions.factories.SubmissionFactory")
 
     @factory.post_generation
     def replies(self, create, extracted, **kwargs):
diff --git a/scipost_django/profiles/factories.py b/scipost_django/profiles/factories.py
index 2072fad85700a917c0132474819881d40fa56397..66c998f62a52dde04a31f84e6e8157b061c2286c 100644
--- a/scipost_django/profiles/factories.py
+++ b/scipost_django/profiles/factories.py
@@ -3,14 +3,14 @@ __license__ = "AGPL v3"
 
 
 import factory
+from common.faker import LazyRandEnum
+from scipost.constants import TITLE_CHOICES
 
 from .models import Profile
 
-from scipost.constants import TITLE_CHOICES
-
 
 class ProfileFactory(factory.django.DjangoModelFactory):
-    title = factory.Iterator(TITLE_CHOICES, getter=lambda c: c[0])
+    title = LazyRandEnum(TITLE_CHOICES)
     first_name = factory.Faker("first_name")
     last_name = factory.Faker("last_name")
 
diff --git a/scipost_django/scipost/factories.py b/scipost_django/scipost/factories.py
index 03163f3b743b4825db5d8a8738d58ccd3549ac31..5a7c0cb78547b665dd28843ad05ab41b13a72d6d 100644
--- a/scipost_django/scipost/factories.py
+++ b/scipost_django/scipost/factories.py
@@ -7,27 +7,36 @@ import pytz
 
 from django.contrib.auth import get_user_model
 from django.contrib.auth.models import Group
+from profiles.factories import ProfileFactory
 
-from common.helpers import generate_orcid
-from submissions.models import Submission
-
+from .constants import NORMAL_CONTRIBUTOR
 from .models import Contributor, Remark, TOTPDevice
-from .constants import TITLE_CHOICES, NORMAL_CONTRIBUTOR
+from common.faker import fake
 
 
 class ContributorFactory(factory.django.DjangoModelFactory):
-    profile = factory.SubFactory("profiles.factories.ProfileFactory")
+    class Meta:
+        model = Contributor
+        django_get_or_create = ("user",)
+
     user = factory.SubFactory("scipost.factories.UserFactory", contributor=None)
+    profile = factory.SubFactory(
+        ProfileFactory,
+        first_name=factory.SelfAttribute("..user.first_name"),
+        last_name=factory.SelfAttribute("..user.last_name"),
+    )
     invitation_key = factory.Faker("md5")
     activation_key = factory.Faker("md5")
     key_expires = factory.Faker("future_datetime", tzinfo=pytz.utc)
     status = NORMAL_CONTRIBUTOR  # normal user
     address = factory.Faker("address")
-    # vetted_by = factory.Iterator(Contributor.objects.all())
 
-    class Meta:
-        model = Contributor
-        django_get_or_create = ("user",)
+    @classmethod
+    def from_profile(cls, profile):
+        return cls(
+            user__first_name=profile.first_name,
+            user__last_name=profile.last_name,
+        )
 
 
 class VettingEditorFactory(ContributorFactory):
@@ -39,11 +48,15 @@ class VettingEditorFactory(ContributorFactory):
 
 
 class UserFactory(factory.django.DjangoModelFactory):
-    username = factory.Faker("user_name")
-    password = factory.PostGenerationMethodCall("set_password", "adm1n")
-    email = factory.Faker("safe_email")
     first_name = factory.Faker("first_name")
     last_name = factory.Faker("last_name")
+    username = factory.LazyAttribute(
+        lambda self: "{first_char}{last_name}".format(
+            first_char=self.first_name[0].lower(), last_name=self.last_name.lower()
+        )
+    )
+    password = factory.PostGenerationMethodCall("set_password", "adm1n")
+    email = factory.Faker("safe_email")
     is_active = True
 
     # When user object is created, associate new Contributor object to it.
@@ -70,15 +83,15 @@ class UserFactory(factory.django.DjangoModelFactory):
 class TOTPDeviceFactory(factory.django.DjangoModelFactory):
     user = factory.SubFactory("scipost.factories.UserFactory")
     name = factory.Faker("pystr")
-    token = factory.Faker("md5")
+    token = factory.LazyFunction(lambda: fake.md5()[:16])
 
     class Meta:
         model = TOTPDevice
 
 
 class SubmissionRemarkFactory(factory.django.DjangoModelFactory):
-    contributor = factory.Iterator(Contributor.objects.all())
-    submission = factory.Iterator(Submission.objects.all())
+    contributor = factory.SubFactory(ContributorFactory)
+    submission = factory.SubFactory("submissions.factories.SubmissionFactory")
     date = factory.Faker("date_time_this_decade")
     remark = factory.Faker("paragraph")
 
diff --git a/scipost_django/submissions/factories/submission.py b/scipost_django/submissions/factories/submission.py
index ee39318fa67f8949c4fc6f411640060a5f961d67..7cdcfd44fb40fa784249c8ab39b4401b9e65bcd7 100644
--- a/scipost_django/submissions/factories/submission.py
+++ b/scipost_django/submissions/factories/submission.py
@@ -7,8 +7,13 @@ import pytz
 import random
 
 from faker import Faker
+from common.faker import LazyRandEnum, fake
+from journals.factories import JournalFactory
+from ontology.factories import AcademicFieldFactory
+from preprints.factories import PreprintFactory
 
 from scipost.constants import SCIPOST_APPROACHES
+from scipost.factories import ContributorFactory
 from scipost.models import Contributor
 from comments.factories import SubmissionCommentFactory
 from journals.models import Journal
@@ -23,29 +28,24 @@ class SubmissionFactory(factory.django.DjangoModelFactory):
     """
 
     author_list = factory.Faker("name")
-    submitted_by = factory.Iterator(Contributor.objects.all())
-    submitted_to = factory.Iterator(Journal.objects.all())
+    submitted_by = factory.SubFactory(ContributorFactory)
+    submitted_to = factory.SubFactory(JournalFactory)
     title = factory.Faker("sentence")
     abstract = factory.Faker("paragraph", nb_sentences=10)
     list_of_changes = factory.Faker("paragraph", nb_sentences=10)
-    acad_field = factory.Iterator(AcademicField.objects.all())
-    approaches = factory.Iterator(
-        SCIPOST_APPROACHES,
-        getter=lambda c: [
-            c[0],
-        ],
-    )
+    acad_field = factory.SubFactory(AcademicFieldFactory)
+    approaches = [LazyRandEnum(SCIPOST_APPROACHES)]
     abstract = factory.Faker("paragraph")
     author_comments = factory.Faker("paragraph")
     remarks_for_editors = factory.Faker("paragraph")
     thread_hash = factory.Faker("uuid4")
     submission_date = factory.Faker("date_time_this_decade", tzinfo=pytz.utc)
     latest_activity = factory.LazyAttribute(
-        lambda o: Faker().date_time_between(
-            start_date=o.submission_date, end_date="now", tzinfo=pytz.UTC
+        lambda self: fake.date_time_between(
+            start_date=self.submission_date, end_date="now", tzinfo=pytz.UTC
         )
     )
-    preprint = factory.SubFactory("preprints.factories.PreprintFactory")
+    preprint = factory.SubFactory(PreprintFactory)
 
     visible_public = True
     visible_pool = False
@@ -53,49 +53,49 @@ class SubmissionFactory(factory.django.DjangoModelFactory):
     class Meta:
         model = Submission
 
-    @classmethod
-    def create(cls, **kwargs):
-        if Contributor.objects.count() < 5:
-            from scipost.factories import ContributorFactory
-
-            ContributorFactory.create_batch(5)
-        if Journal.objects.count() < 3:
-            from journals.factories import JournalFactory
-
-            JournalFactory.create_batch(3)
-        if AcademicField.objects.count() < 10:
-            from ontology.factories import AcademicFieldFactory
-
-            AcademicFieldFactory.create_batch(10)
-        if Specialty.objects.count() < 5:
-            from ontology.factories import SpecialtyFactory
-
-            SpecialtyFactory.create_batch(5)
-        return super().create(**kwargs)
-
-    @factory.post_generation
-    def add_specialties(self, create, extracted, **kwargs):
-        if create:
-            self.specialties.set(Specialty.objects.order_by("?")[:3])
-
-    @factory.post_generation
-    def contributors(self, create, extracted, **kwargs):
-        contribs = Contributor.objects.all()
-        if self.editor_in_charge:
-            contribs = contribs.exclude(id=self.editor_in_charge.id)
-        contribs = contribs.order_by("?")[: random.randint(1, 6)]
-
-        # Auto-add the submitter as an author
-        self.submitted_by = contribs[0]
-        self.author_list = ", ".join(
-            ["%s %s" % (c.user.first_name, c.user.last_name) for c in contribs]
-        )
-
-        if not create:
-            return
-
-        # Add three random authors
-        self.authors.add(*contribs)
+    # @classmethod
+    # def create(cls, **kwargs):
+    #     if Contributor.objects.count() < 5:
+    #         from scipost.factories import ContributorFactory
+
+    #         ContributorFactory.create_batch(5)
+    #     if Journal.objects.count() < 3:
+    #         from journals.factories import JournalFactory
+
+    #         JournalFactory.create_batch(3)
+    #     if AcademicField.objects.count() < 10:
+    #         from ontology.factories import AcademicFieldFactory
+
+    #         AcademicFieldFactory.create_batch(10)
+    #     if Specialty.objects.count() < 5:
+    #         from ontology.factories import SpecialtyFactory
+
+    #         SpecialtyFactory.create_batch(5)
+    #     return super().create(**kwargs)
+
+    # @factory.post_generation
+    # def add_specialties(self, create, extracted, **kwargs):
+    #     if create:
+    #         self.specialties.set(Specialty.objects.order_by("?")[:3])
+
+    # @factory.post_generation
+    # def contributors(self, create, extracted, **kwargs):
+    #     contribs = Contributor.objects.all()
+    #     if self.editor_in_charge:
+    #         contribs = contribs.exclude(id=self.editor_in_charge.id)
+    #     contribs = contribs.order_by("?")[: random.randint(1, 6)]
+
+    #     # Auto-add the submitter as an author
+    #     self.submitted_by = contribs[0]
+    #     self.author_list = ", ".join(
+    #         ["%s %s" % (c.user.first_name, c.user.last_name) for c in contribs]
+    #     )
+
+    #     if not create:
+    #         return
+
+    #     # Add three random authors
+    #     self.authors.add(*contribs)
 
 
 class SeekingAssignmentSubmissionFactory(SubmissionFactory):