diff --git a/common/djangoapps/course_modes/tests/test_models.py b/common/djangoapps/course_modes/tests/test_models.py
index 90dab57cae60b6d8ff45c56263ffd5be3c1ef82d..c3724623831834155db153dd4966c31af8d2a1a1 100644
--- a/common/djangoapps/course_modes/tests/test_models.py
+++ b/common/djangoapps/course_modes/tests/test_models.py
@@ -29,6 +29,11 @@ class CourseModeModelTest(TestCase):
     """
     Tests for the CourseMode model
     """
+    NOW = 'now'
+    DATES = {
+        NOW: datetime.now(),
+        None: None,
+    }
 
     def setUp(self):
         super(CourseModeModelTest, self).setUp()
@@ -317,10 +322,11 @@ class CourseModeModelTest(TestCase):
             CourseMode.PROFESSIONAL,
             CourseMode.NO_ID_PROFESSIONAL_MODE
         ),
-        (datetime.now(), None),
+        (NOW, None),
     ))
     @ddt.unpack
-    def test_invalid_mode_expiration(self, mode_slug, exp_dt):
+    def test_invalid_mode_expiration(self, mode_slug, exp_dt_name):
+        exp_dt = self.DATES[exp_dt_name]
         is_error_expected = CourseMode.is_professional_slug(mode_slug) and exp_dt is not None
         try:
             self.create_mode(mode_slug=mode_slug, mode_name=mode_slug.title(), expiration_datetime=exp_dt, min_price=10)
diff --git a/common/djangoapps/student/roles.py b/common/djangoapps/student/roles.py
index c14ea5b2340ad021c9dff28347ec256b3be2918b..56517f7984f32a38b34aea3d5682d5b556e5c338 100644
--- a/common/djangoapps/student/roles.py
+++ b/common/djangoapps/student/roles.py
@@ -234,12 +234,16 @@ class CourseRole(RoleBase):
     def course_group_already_exists(self, course_key):
         return CourseAccessRole.objects.filter(org=course_key.org, course_id=course_key).exists()
 
+    def __repr__(self):
+        return '<{}: course_key={}>'.format(self.__class__.__name__, self.course_key)
+
 
 class OrgRole(RoleBase):
     """
     A named role in a particular org independent of course
     """
-    pass
+    def __repr__(self):
+        return '<{}>'.format(self.__class__.__name__)
 
 
 @register_access_role
diff --git a/common/djangoapps/student/tests/test_verification_status.py b/common/djangoapps/student/tests/test_verification_status.py
index 75f58848cd988fc2d3e7186b01ea32ac830d6eac..40cbcb84337bfa93bf3b756ffa9dc490ecba1b97 100644
--- a/common/djangoapps/student/tests/test_verification_status.py
+++ b/common/djangoapps/student/tests/test_verification_status.py
@@ -33,8 +33,13 @@ from xmodule.modulestore.tests.factories import CourseFactory
 class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
     """Tests for per-course verification status on the dashboard. """
 
-    PAST = datetime.now(UTC) - timedelta(days=5)
-    FUTURE = datetime.now(UTC) + timedelta(days=5)
+    PAST = 'past'
+    FUTURE = 'future'
+    DATES = {
+        PAST: datetime.now(UTC) - timedelta(days=5),
+        FUTURE: datetime.now(UTC) + timedelta(days=5),
+        None: None,
+    }
 
     URLCONF_MODULES = ['verify_student.urls']
 
@@ -91,14 +96,14 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
         self._assert_course_verification_status(VERIFY_STATUS_NEED_TO_VERIFY)
 
     def test_need_to_verify_expiration(self):
-        self._setup_mode_and_enrollment(self.FUTURE, "verified")
+        self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
         response = self.client.get(self.dashboard_url)
         self.assertContains(response, self.BANNER_ALT_MESSAGES[VERIFY_STATUS_NEED_TO_VERIFY])
         self.assertContains(response, "You only have 4 days left to verify for this course.")
 
     @ddt.data(None, FUTURE)
     def test_waiting_approval(self, expiration):
-        self._setup_mode_and_enrollment(expiration, "verified")
+        self._setup_mode_and_enrollment(self.DATES[expiration], "verified")
 
         # The student has submitted a photo verification
         attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
@@ -110,7 +115,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
 
     @ddt.data(None, FUTURE)
     def test_fully_verified(self, expiration):
-        self._setup_mode_and_enrollment(expiration, "verified")
+        self._setup_mode_and_enrollment(self.DATES[expiration], "verified")
 
         # The student has an approved verification
         attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
@@ -127,7 +132,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
 
     def test_missed_verification_deadline(self):
         # Expiration date in the past
-        self._setup_mode_and_enrollment(self.PAST, "verified")
+        self._setup_mode_and_enrollment(self.DATES[self.PAST], "verified")
 
         # The student does NOT have an approved verification
         # so the status should show that the student missed the deadline.
@@ -135,7 +140,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
 
     def test_missed_verification_deadline_verification_was_expired(self):
         # Expiration date in the past
-        self._setup_mode_and_enrollment(self.PAST, "verified")
+        self._setup_mode_and_enrollment(self.DATES[self.PAST], "verified")
 
         # Create a verification, but the expiration date of the verification
         # occurred before the deadline.
@@ -143,7 +148,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
         attempt.mark_ready()
         attempt.submit()
         attempt.approve()
-        attempt.created_at = self.PAST - timedelta(days=900)
+        attempt.created_at = self.DATES[self.PAST] - timedelta(days=900)
         attempt.save()
 
         # The student didn't have an approved verification at the deadline,
@@ -152,14 +157,14 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
 
     def test_missed_verification_deadline_but_later_verified(self):
         # Expiration date in the past
-        self._setup_mode_and_enrollment(self.PAST, "verified")
+        self._setup_mode_and_enrollment(self.DATES[self.PAST], "verified")
 
         # Successfully verify, but after the deadline has already passed
         attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
         attempt.mark_ready()
         attempt.submit()
         attempt.approve()
-        attempt.created_at = self.PAST - timedelta(days=900)
+        attempt.created_at = self.DATES[self.PAST] - timedelta(days=900)
         attempt.save()
 
         # The student didn't have an approved verification at the deadline,
@@ -168,7 +173,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
 
     def test_verification_denied(self):
         # Expiration date in the future
-        self._setup_mode_and_enrollment(self.FUTURE, "verified")
+        self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
 
         # Create a verification with the specified status
         attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
@@ -182,7 +187,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
 
     def test_verification_error(self):
         # Expiration date in the future
-        self._setup_mode_and_enrollment(self.FUTURE, "verified")
+        self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
 
         # Create a verification with the specified status
         attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
@@ -196,7 +201,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
     @override_settings(VERIFY_STUDENT={"DAYS_GOOD_FOR": 5, "EXPIRING_SOON_WINDOW": 10})
     def test_verification_will_expire_by_deadline(self):
         # Expiration date in the future
-        self._setup_mode_and_enrollment(self.FUTURE, "verified")
+        self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
 
         # Create a verification attempt that:
         # 1) Is current (submitted in the last year)
@@ -213,7 +218,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
     @override_settings(VERIFY_STUDENT={"DAYS_GOOD_FOR": 5, "EXPIRING_SOON_WINDOW": 10})
     def test_reverification_submitted_with_current_approved_verificaiton(self):
         # Expiration date in the future
-        self._setup_mode_and_enrollment(self.FUTURE, "verified")
+        self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
 
         # Create a verification attempt that is approved but expiring soon
         attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
@@ -236,7 +241,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
 
     def test_verification_occurred_after_deadline(self):
         # Expiration date in the past
-        self._setup_mode_and_enrollment(self.PAST, "verified")
+        self._setup_mode_and_enrollment(self.DATES[self.PAST], "verified")
 
         # The deadline has passed, and we've asked the student
         # to reverify (through the support team).
@@ -250,7 +255,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
     def test_with_two_verifications(self):
         # checking if a user has two verification and but most recent verification course deadline is expired
 
-        self._setup_mode_and_enrollment(self.FUTURE, "verified")
+        self._setup_mode_and_enrollment(self.DATES[self.FUTURE], "verified")
 
         # The student has an approved verification
         attempt = SoftwareSecurePhotoVerification.objects.create(user=self.user)
@@ -274,7 +279,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
         CourseModeFactory.create(
             course_id=course2.id,
             mode_slug="verified",
-            expiration_datetime=self.PAST
+            expiration_datetime=self.DATES[self.PAST]
         )
         CourseEnrollmentFactory(
             course_id=course2.id,
diff --git a/common/djangoapps/student/tests/test_views.py b/common/djangoapps/student/tests/test_views.py
index c74b5e690d0e2cb07eee318d9b12e62a8a15cca6..073eb5c7958146b3fcebdbe2281ea62a4380c3a4 100644
--- a/common/djangoapps/student/tests/test_views.py
+++ b/common/djangoapps/student/tests/test_views.py
@@ -297,19 +297,18 @@ class StudentDashboardTests(SharedModuleStoreTestCase):
     @patch.multiple('django.conf.settings', **MOCK_SETTINGS)
     @ddt.data(
         *itertools.product(
-            [TOMORROW],
             [True, False],
             [True, False],
             [ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split],
         )
     )
     @ddt.unpack
-    def test_sharing_icons_for_future_course(self, start_date, set_marketing, set_social_sharing, modulestore_type):
+    def test_sharing_icons_for_future_course(self, set_marketing, set_social_sharing, modulestore_type):
         """
         Verify that the course sharing icons show up if course is starting in future and
         any of marketing or social sharing urls are set.
         """
-        self.course = CourseFactory.create(start=start_date, emit_signals=True, default_store=modulestore_type)
+        self.course = CourseFactory.create(start=self.TOMORROW, emit_signals=True, default_store=modulestore_type)
         self.course_enrollment = CourseEnrollmentFactory(course_id=self.course.id, user=self.user)
         self.set_course_sharing_urls(set_marketing, set_social_sharing)
 
diff --git a/common/djangoapps/util/tests/test_db.py b/common/djangoapps/util/tests/test_db.py
index a3c780149080ffc351a7de89b31c654bcfe0ea30..3830d178eae8d862204bb3c08a77a1c70747413a 100644
--- a/common/djangoapps/util/tests/test_db.py
+++ b/common/djangoapps/util/tests/test_db.py
@@ -31,20 +31,26 @@ class TransactionManagersTestCase(TransactionTestCase):
 
     To test do: "./manage.py lms --settings=test_with_mysql test util.tests.test_db"
     """
+    DECORATORS = {
+        'outer_atomic': outer_atomic(),
+        'outer_atomic_read_committed': outer_atomic(read_committed=True),
+        'commit_on_success': commit_on_success(),
+        'commit_on_success_read_committed': commit_on_success(read_committed=True),
+    }
 
     @ddt.data(
-        (outer_atomic(), IntegrityError, None, True),
-        (outer_atomic(read_committed=True), type(None), False, True),
-        (commit_on_success(), IntegrityError, None, True),
-        (commit_on_success(read_committed=True), type(None), False, True),
+        ('outer_atomic', IntegrityError, None, True),
+        ('outer_atomic_read_committed', type(None), False, True),
+        ('commit_on_success', IntegrityError, None, True),
+        ('commit_on_success_read_committed', type(None), False, True),
     )
     @ddt.unpack
-    def test_concurrent_requests(self, transaction_decorator, exception_class, created_in_1, created_in_2):
+    def test_concurrent_requests(self, transaction_decorator_name, exception_class, created_in_1, created_in_2):
         """
         Test that when isolation level is set to READ COMMITTED get_or_create()
         for the same row in concurrent requests does not raise an IntegrityError.
         """
-
+        transaction_decorator = self.DECORATORS[transaction_decorator_name]
         if connection.vendor != 'mysql':
             raise unittest.SkipTest('Only works on MySQL.')
 
diff --git a/common/test/utils.py b/common/test/utils.py
index cab85a99703fbb767ca81ce8b6f06cab0bea87dd..0457ead3ebacf9526806ac201a1ea361991b15a6 100644
--- a/common/test/utils.py
+++ b/common/test/utils.py
@@ -1,6 +1,7 @@
 """
 General testing utilities.
 """
+import functools
 import sys
 from contextlib import contextmanager
 
@@ -124,3 +125,29 @@ class MockS3Mixin(object):
     def tearDown(self):
         self._mock_s3.stop()
         super(MockS3Mixin, self).tearDown()
+
+
+class reprwrapper(object):
+    """
+    Wrapper class for functions that need a normalized string representation.
+    """
+    def __init__(self, func):
+        self._func = func
+        self.repr = 'Func: {}'.format(func.__name__)
+        functools.update_wrapper(self, func)
+
+    def __call__(self, *args, **kw):
+        return self._func(*args, **kw)
+
+    def __repr__(self):
+        return self.repr
+
+
+def normalize_repr(func):
+    """
+    Function decorator used to normalize its string representation.
+    Used to wrap functions used as ddt parameters, so pytest-xdist
+    doesn't complain about the sequence of discovered tests differing
+    between worker processes.
+    """
+    return reprwrapper(func)
diff --git a/lms/djangoapps/certificates/tests/test_queue.py b/lms/djangoapps/certificates/tests/test_queue.py
index dbc84cf6a4d2f883a2f75a4b9a4d95fa504e7ac3..dac452ceca487b7edcb8cf8a1717528ea2b72550 100644
--- a/lms/djangoapps/certificates/tests/test_queue.py
+++ b/lms/djangoapps/certificates/tests/test_queue.py
@@ -207,42 +207,42 @@ class XQueueCertInterfaceAddCertificateTest(ModuleStoreTestCase):
         # Eligible and should stay that way
         (
             CertificateStatuses.downloadable,
-            datetime.now(pytz.UTC) - timedelta(days=2),
+            timedelta(days=-2),
             'Pass',
             CertificateStatuses.generating
         ),
         # Ensure that certs in the wrong state can be fixed by regeneration
         (
             CertificateStatuses.downloadable,
-            datetime.now(pytz.UTC) - timedelta(hours=1),
+            timedelta(hours=-1),
             'Pass',
             CertificateStatuses.audit_passing
         ),
         # Ineligible and should stay that way
         (
             CertificateStatuses.audit_passing,
-            datetime.now(pytz.UTC) - timedelta(hours=1),
+            timedelta(hours=-1),
             'Pass',
             CertificateStatuses.audit_passing
         ),
         # As above
         (
             CertificateStatuses.audit_notpassing,
-            datetime.now(pytz.UTC) - timedelta(hours=1),
+            timedelta(hours=-1),
             'Pass',
             CertificateStatuses.audit_passing
         ),
         # As above
         (
             CertificateStatuses.audit_notpassing,
-            datetime.now(pytz.UTC) - timedelta(hours=1),
+            timedelta(hours=-1),
             None,
             CertificateStatuses.audit_notpassing
         ),
     )
     @ddt.unpack
     @override_settings(AUDIT_CERT_CUTOFF_DATE=datetime.now(pytz.UTC) - timedelta(days=1))
-    def test_regen_audit_certs_eligibility(self, status, created_date, grade, expected_status):
+    def test_regen_audit_certs_eligibility(self, status, created_delta, grade, expected_status):
         """
         Test that existing audit certificates remain eligible even if cert
         generation is re-run.
@@ -254,6 +254,7 @@ class XQueueCertInterfaceAddCertificateTest(ModuleStoreTestCase):
             is_active=True,
             mode=CourseMode.AUDIT,
         )
+        created_date = datetime.now(pytz.UTC) + created_delta
         with freezegun.freeze_time(created_date):
             GeneratedCertificateFactory(
                 user=self.user_2,
diff --git a/lms/djangoapps/commerce/api/v1/tests/test_views.py b/lms/djangoapps/commerce/api/v1/tests/test_views.py
index 569f70ee9e817b4803dfbcde8e3f22d71075444a..177d1ea9f3552f85e7106b48433a722956b22a2d 100644
--- a/lms/djangoapps/commerce/api/v1/tests/test_views.py
+++ b/lms/djangoapps/commerce/api/v1/tests/test_views.py
@@ -106,6 +106,11 @@ class CourseListViewTests(CourseApiViewTestMixin, ModuleStoreTestCase):
 @ddt.ddt
 class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase):
     """ Tests for CourseRetrieveUpdateView. """
+    NOW = 'now'
+    DATES = {
+        NOW: datetime.now(),
+        None: None,
+    }
 
     def setUp(self):
         super(CourseRetrieveUpdateViewTests, self).setUp()
@@ -276,12 +281,13 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
 
     @ddt.data(*itertools.product(
         ('honor', 'audit', 'verified', 'professional', 'no-id-professional'),
-        (datetime.now(), None),
+        (NOW, None),
     ))
     @ddt.unpack
-    def test_update_professional_expiration(self, mode_slug, expiration_datetime):
+    def test_update_professional_expiration(self, mode_slug, expiration_datetime_name):
         """ Verify that pushing a mode with a professional certificate and an expiration datetime
         will be rejected (this is not allowed). """
+        expiration_datetime = self.DATES[expiration_datetime_name]
         mode = self._serialize_course_mode(
             CourseMode(
                 mode_slug=mode_slug,
diff --git a/lms/djangoapps/courseware/tests/test_access.py b/lms/djangoapps/courseware/tests/test_access.py
index d7a4732d46235db7af57beb832b509c4578a68ab..acc4948a9e13eb8667cc4605f202882b15be12d7 100644
--- a/lms/djangoapps/courseware/tests/test_access.py
+++ b/lms/djangoapps/courseware/tests/test_access.py
@@ -162,9 +162,14 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
     """
     Tests for the various access controls on the student dashboard
     """
-    TOMORROW = datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1)
-    YESTERDAY = datetime.datetime.now(pytz.utc) - datetime.timedelta(days=1)
+    TOMORROW = 'tomorrow'
+    YESTERDAY = 'yesterday'
     MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
+    DATES = {
+        TOMORROW: datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1),
+        YESTERDAY: datetime.datetime.now(pytz.utc) - datetime.timedelta(days=1),
+        None: None,
+    }
 
     def setUp(self):
         super(AccessTestCase, self).setUp()
@@ -439,7 +444,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
         mock_unit = Mock(location=self.course.location, user_partitions=[])
         mock_unit._class_tags = {}  # Needed for detached check in _has_access_descriptor
         mock_unit.visible_to_staff_only = visible_to_staff_only
-        mock_unit.start = start
+        mock_unit.start = self.DATES[start]
         mock_unit.merged_group_access = {}
 
         self.verify_access(mock_unit, expected_access, expected_error_type)
@@ -448,7 +453,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
         mock_unit = Mock(user_partitions=[])
         mock_unit._class_tags = {}
         mock_unit.days_early_for_beta = 2
-        mock_unit.start = self.TOMORROW
+        mock_unit.start = self.DATES[self.TOMORROW]
         mock_unit.visible_to_staff_only = False
         mock_unit.merged_group_access = {}
 
@@ -465,7 +470,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
         mock_unit = Mock(location=self.course.location, user_partitions=[])
         mock_unit._class_tags = {}  # Needed for detached check in _has_access_descriptor
         mock_unit.visible_to_staff_only = False
-        mock_unit.start = start
+        mock_unit.start = self.DATES[start]
         mock_unit.merged_group_access = {}
 
         self.verify_access(mock_unit, True)
@@ -486,7 +491,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
         mock_unit = Mock(location=self.course.location, user_partitions=[])
         mock_unit._class_tags = {}  # Needed for detached check in _has_access_descriptor
         mock_unit.visible_to_staff_only = False
-        mock_unit.start = start
+        mock_unit.start = self.DATES[start]
         mock_unit.merged_group_access = {}
 
         self.verify_access(mock_unit, expected_access, expected_error_type)
diff --git a/lms/djangoapps/courseware/tests/test_courses.py b/lms/djangoapps/courseware/tests/test_courses.py
index f9229dbda57bc20a61f2fd8d1e751c441888ca46..50c6f137ed5d0a2c7ba0422b4e30c74273e53c51 100644
--- a/lms/djangoapps/courseware/tests/test_courses.py
+++ b/lms/djangoapps/courseware/tests/test_courses.py
@@ -49,6 +49,12 @@ TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
 class CoursesTest(ModuleStoreTestCase):
     """Test methods related to fetching courses."""
     ENABLED_SIGNALS = ['course_published']
+    GET_COURSE_WITH_ACCESS = 'get_course_with_access'
+    GET_COURSE_OVERVIEW_WITH_ACCESS = 'get_course_overview_with_access'
+    COURSE_ACCESS_FUNCS = {
+        GET_COURSE_WITH_ACCESS: get_course_with_access,
+        GET_COURSE_OVERVIEW_WITH_ACCESS: get_course_overview_with_access,
+    }
 
     @override_settings(CMS_BASE=CMS_BASE_TEST)
     def test_get_cms_course_block_link(self):
@@ -64,8 +70,9 @@ class CoursesTest(ModuleStoreTestCase):
         cms_url = u"//{}/course/{}".format(CMS_BASE_TEST, unicode(self.course.location))
         self.assertEqual(cms_url, get_cms_block_link(self.course, 'course'))
 
-    @ddt.data(get_course_with_access, get_course_overview_with_access)
-    def test_get_course_func_with_access_error(self, course_access_func):
+    @ddt.data(GET_COURSE_WITH_ACCESS, GET_COURSE_OVERVIEW_WITH_ACCESS)
+    def test_get_course_func_with_access_error(self, course_access_func_name):
+        course_access_func = self.COURSE_ACCESS_FUNCS[course_access_func_name]
         user = UserFactory.create()
         course = CourseFactory.create(visible_to_staff_only=True)
 
@@ -76,11 +83,12 @@ class CoursesTest(ModuleStoreTestCase):
         self.assertFalse(error.exception.access_response.has_access)
 
     @ddt.data(
-        (get_course_with_access, 1),
-        (get_course_overview_with_access, 0),
+        (GET_COURSE_WITH_ACCESS, 1),
+        (GET_COURSE_OVERVIEW_WITH_ACCESS, 0),
     )
     @ddt.unpack
-    def test_get_course_func_with_access(self, course_access_func, num_mongo_calls):
+    def test_get_course_func_with_access(self, course_access_func_name, num_mongo_calls):
+        course_access_func = self.COURSE_ACCESS_FUNCS[course_access_func_name]
         user = UserFactory.create()
         course = CourseFactory.create(emit_signals=True)
         with check_mongo_calls(num_mongo_calls):
diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py
index 3c215bbff2ae1a57dd288dce8e5cc3bfe7dd919a..6ed47a59cdf456a9ee086b55a7e7c62e028c6b1b 100644
--- a/lms/djangoapps/courseware/tests/test_module_render.py
+++ b/lms/djangoapps/courseware/tests/test_module_render.py
@@ -1570,11 +1570,11 @@ class TestStaffDebugInfo(SharedModuleStoreTestCase):
 
 PER_COURSE_ANONYMIZED_DESCRIPTORS = (LTIDescriptor, )
 
-# The "set" here is to work around the bug that load_classes returns duplicates for multiply-delcared classes.
-PER_STUDENT_ANONYMIZED_DESCRIPTORS = set(
+# The "set" here is to work around the bug that load_classes returns duplicates for multiply-declared classes.
+PER_STUDENT_ANONYMIZED_DESCRIPTORS = sorted(set(
     class_ for (name, class_) in XModuleDescriptor.load_classes()
     if not issubclass(class_, PER_COURSE_ANONYMIZED_DESCRIPTORS)
-)
+), key=str)
 
 
 @attr(shard=1)
diff --git a/lms/djangoapps/courseware/tests/test_video_handlers.py b/lms/djangoapps/courseware/tests/test_video_handlers.py
index a4c04cb795fe854dd81b7175116178d5ea91406f..83d555dda56b34787dc2dd14c75c5bae37457281 100644
--- a/lms/djangoapps/courseware/tests/test_video_handlers.py
+++ b/lms/djangoapps/courseware/tests/test_video_handlers.py
@@ -13,6 +13,7 @@ from mock import MagicMock, Mock, patch
 from nose.plugins.attrib import attr
 from webob import Request
 
+from common.test.utils import normalize_repr
 from openedx.core.djangoapps.contentserver.caching import del_cached_content
 from xmodule.contentstore.content import StaticContent
 from xmodule.contentstore.django import contentstore
@@ -105,6 +106,7 @@ def _upload_file(subs_file, location, filename):
     del_cached_content(content.location)
 
 
+@normalize_repr
 def attach_sub(item, filename):
     """
     Attach `en` transcript.
@@ -112,6 +114,7 @@ def attach_sub(item, filename):
     item.sub = filename
 
 
+@normalize_repr
 def attach_bumper_transcript(item, filename, lang="en"):
     """
     Attach bumper transcript.
diff --git a/lms/djangoapps/courseware/tests/test_video_mongo.py b/lms/djangoapps/courseware/tests/test_video_mongo.py
index 807a7e23cbde008ad3f2fccec6965784ad39440b..6f13abc44fc192a51e1d951f50b61289c1f71170 100644
--- a/lms/djangoapps/courseware/tests/test_video_mongo.py
+++ b/lms/djangoapps/courseware/tests/test_video_mongo.py
@@ -17,6 +17,7 @@ from path import Path as path
 
 from xmodule.contentstore.content import StaticContent
 from xmodule.exceptions import NotFoundError
+from xmodule.modulestore import ModuleStoreEnum
 from xmodule.modulestore.inheritance import own_metadata
 from xmodule.modulestore.tests.django_utils import TEST_DATA_MONGO_MODULESTORE, TEST_DATA_SPLIT_MODULESTORE
 from xmodule.tests.test_import import DummySystem
@@ -29,6 +30,11 @@ from .helpers import BaseTestXmodule
 from .test_video_handlers import TestVideo
 from .test_video_xml import SOURCE_XML
 
+MODULESTORES = {
+    ModuleStoreEnum.Type.mongo: TEST_DATA_MONGO_MODULESTORE,
+    ModuleStoreEnum.Type.split: TEST_DATA_SPLIT_MODULESTORE,
+}
+
 
 @attr(shard=1)
 class TestVideoYouTube(TestVideo):
@@ -1162,14 +1168,14 @@ class TestEditorSavedMethod(BaseTestXmodule):
         self.test_dir = path(__file__).abspath().dirname().dirname().dirname().dirname().dirname()
         self.file_path = self.test_dir + '/common/test/data/uploads/' + self.file_name
 
-    @ddt.data(TEST_DATA_MONGO_MODULESTORE, TEST_DATA_SPLIT_MODULESTORE)
+    @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
     def test_editor_saved_when_html5_sub_not_exist(self, default_store):
         """
         When there is youtube_sub exist but no html5_sub present for
         html5_sources, editor_saved function will generate new html5_sub
         for video.
         """
-        self.MODULESTORE = default_store  # pylint: disable=invalid-name
+        self.MODULESTORE = MODULESTORES[default_store]  # pylint: disable=invalid-name
         self.initialize_module(metadata=self.metadata)
         item = self.store.get_item(self.item_descriptor.location)
         with open(self.file_path, "r") as myfile:
@@ -1184,13 +1190,13 @@ class TestEditorSavedMethod(BaseTestXmodule):
         self.assertIsInstance(Transcript.get_asset(item.location, 'subs_3_yD_cEKoCk.srt.sjson'), StaticContent)
         self.assertIsInstance(Transcript.get_asset(item.location, 'subs_video.srt.sjson'), StaticContent)
 
-    @ddt.data(TEST_DATA_MONGO_MODULESTORE, TEST_DATA_SPLIT_MODULESTORE)
+    @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
     def test_editor_saved_when_youtube_and_html5_subs_exist(self, default_store):
         """
         When both youtube_sub and html5_sub already exist then no new
         sub will be generated by editor_saved function.
         """
-        self.MODULESTORE = default_store
+        self.MODULESTORE = MODULESTORES[default_store]
         self.initialize_module(metadata=self.metadata)
         item = self.store.get_item(self.item_descriptor.location)
         with open(self.file_path, "r") as myfile:
@@ -1205,12 +1211,12 @@ class TestEditorSavedMethod(BaseTestXmodule):
             item.editor_saved(self.user, old_metadata, None)
             self.assertFalse(manage_video_subtitles_save.called)
 
-    @ddt.data(TEST_DATA_MONGO_MODULESTORE, TEST_DATA_SPLIT_MODULESTORE)
+    @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
     def test_editor_saved_with_unstripped_video_id(self, default_store):
         """
         Verify editor saved when video id contains spaces/tabs.
         """
-        self.MODULESTORE = default_store
+        self.MODULESTORE = MODULESTORES[default_store]
         stripped_video_id = unicode(uuid4())
         unstripped_video_id = u'{video_id}{tabs}'.format(video_id=stripped_video_id, tabs=u'\t\t\t')
         self.metadata.update({
@@ -1226,14 +1232,14 @@ class TestEditorSavedMethod(BaseTestXmodule):
         item.editor_saved(self.user, old_metadata, None)
         self.assertEqual(item.edx_video_id, stripped_video_id)
 
-    @ddt.data(TEST_DATA_MONGO_MODULESTORE, TEST_DATA_SPLIT_MODULESTORE)
+    @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
     @patch('xmodule.video_module.video_module.edxval_api.get_url_for_profile', Mock(return_value='test_yt_id'))
     def test_editor_saved_with_yt_val_profile(self, default_store):
         """
         Verify editor saved overrides `youtube_id_1_0` when a youtube val profile is there
         for a given `edx_video_id`.
         """
-        self.MODULESTORE = default_store
+        self.MODULESTORE = MODULESTORES[default_store]
         self.initialize_module(metadata=self.metadata)
         item = self.store.get_item(self.item_descriptor.location)
         self.assertEqual(item.youtube_id_1_0, '3_yD_cEKoCk')
diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py
index eb08d7934eb24baa887e7385f368b2502d0d0ec3..5cdf69af9b617734ceac6cba03c554a7f0aaf89d 100644
--- a/lms/djangoapps/courseware/tests/test_views.py
+++ b/lms/djangoapps/courseware/tests/test_views.py
@@ -251,6 +251,11 @@ class ViewsTestCase(ModuleStoreTestCase):
     """
     Tests for views.py methods.
     """
+    YESTERDAY = 'yesterday'
+    DATES = {
+        YESTERDAY: datetime.now(UTC) - timedelta(days=1),
+        None: None,
+    }
 
     def setUp(self):
         super(ViewsTestCase, self).setUp()
@@ -751,7 +756,7 @@ class ViewsTestCase(ModuleStoreTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertIn('Financial Assistance Application', response.content)
 
-    @ddt.data(([CourseMode.AUDIT, CourseMode.VERIFIED], CourseMode.AUDIT, True, datetime.now(UTC) - timedelta(days=1)),
+    @ddt.data(([CourseMode.AUDIT, CourseMode.VERIFIED], CourseMode.AUDIT, True, YESTERDAY),
               ([CourseMode.AUDIT, CourseMode.VERIFIED], CourseMode.VERIFIED, True, None),
               ([CourseMode.AUDIT, CourseMode.VERIFIED], CourseMode.AUDIT, False, None),
               ([CourseMode.AUDIT], CourseMode.AUDIT, False, None))
@@ -770,7 +775,7 @@ class ViewsTestCase(ModuleStoreTestCase):
 
         # Create Course Modes
         for mode in course_modes:
-            CourseModeFactory.create(mode_slug=mode, course_id=course.id, expiration_datetime=expiration)
+            CourseModeFactory.create(mode_slug=mode, course_id=course.id, expiration_datetime=self.DATES[expiration])
 
         # Enroll user in the course
         CourseEnrollmentFactory(course_id=course.id, user=self.user, mode=enrollment_mode)
@@ -1705,10 +1710,16 @@ class ProgressPageShowCorrectnessTests(ProgressPageBaseTests):
     # Constants used in the test data
     NOW = datetime.now(UTC)
     DAY_DELTA = timedelta(days=1)
-    YESTERDAY = NOW - DAY_DELTA
-    TODAY = NOW
-    TOMORROW = NOW + DAY_DELTA
+    YESTERDAY = 'yesterday'
+    TODAY = 'today'
+    TOMORROW = 'tomorrow'
     GRADER_TYPE = 'Homework'
+    DATES = {
+        YESTERDAY: NOW - DAY_DELTA,
+        TODAY: NOW,
+        TOMORROW: NOW + DAY_DELTA,
+        None: None,
+    }
 
     def setUp(self):
         super(ProgressPageShowCorrectnessTests, self).setUp()
@@ -1853,12 +1864,12 @@ class ProgressPageShowCorrectnessTests(ProgressPageBaseTests):
         (ShowCorrectness.PAST_DUE, TOMORROW, True),
     )
     @ddt.unpack
-    def test_progress_page_no_problem_scores(self, show_correctness, due_date, graded):
+    def test_progress_page_no_problem_scores(self, show_correctness, due_date_name, graded):
         """
         Test that "no problem scores are present" for a course with no problems,
         regardless of the various show correctness settings.
         """
-        self.setup_course(show_correctness=show_correctness, due_date=due_date, graded=graded)
+        self.setup_course(show_correctness=show_correctness, due_date=self.DATES[due_date_name], graded=graded)
         resp = self._get_progress_page()
 
         # Test that no problem scores are present
@@ -1893,11 +1904,12 @@ class ProgressPageShowCorrectnessTests(ProgressPageBaseTests):
         (ShowCorrectness.PAST_DUE, TOMORROW, True, False),
     )
     @ddt.unpack
-    def test_progress_page_hide_scores_from_learner(self, show_correctness, due_date, graded, show_grades):
+    def test_progress_page_hide_scores_from_learner(self, show_correctness, due_date_name, graded, show_grades):
         """
         Test that problem scores are hidden on progress page when correctness is not available to the learner, and that
         they are visible when it is.
         """
+        due_date = self.DATES[due_date_name]
         self.setup_course(show_correctness=show_correctness, due_date=due_date, graded=graded)
         self.add_problem()
 
@@ -1944,10 +1956,11 @@ class ProgressPageShowCorrectnessTests(ProgressPageBaseTests):
         (ShowCorrectness.PAST_DUE, TOMORROW, True, True),
     )
     @ddt.unpack
-    def test_progress_page_hide_scores_from_staff(self, show_correctness, due_date, graded, show_grades):
+    def test_progress_page_hide_scores_from_staff(self, show_correctness, due_date_name, graded, show_grades):
         """
         Test that problem scores are hidden from staff viewing a learner's progress page only if show_correctness=never.
         """
+        due_date = self.DATES[due_date_name]
         self.setup_course(show_correctness=show_correctness, due_date=due_date, graded=graded)
         self.add_problem()
 
diff --git a/lms/djangoapps/grades/tests/test_scores.py b/lms/djangoapps/grades/tests/test_scores.py
index 286e3a3cad97cebde962b169259d6ac9e3c607a4..c134586c1b21bbcd580d470bcf895c90a5a47173 100644
--- a/lms/djangoapps/grades/tests/test_scores.py
+++ b/lms/djangoapps/grades/tests/test_scores.py
@@ -19,6 +19,35 @@ from xmodule.graders import ProblemScore
 NOW = now()
 
 
+def submission_value_repr(self):
+    """
+    String representation for the SubmissionValue namedtuple which excludes
+    the "created_at" attribute that changes with each execution.  Needed for
+    consistency of ddt-generated test methods across pytest-xdist workers.
+    """
+    return '<SubmissionValue exists={}>'.format(self.exists)
+
+
+def csm_value_repr(self):
+    """
+    String representation for the CSMValue namedtuple which excludes
+    the "created" attribute that changes with each execution.  Needed for
+    consistency of ddt-generated test methods across pytest-xdist workers.
+    """
+    return '<CSMValue exists={} raw_earned={}>'.format(self.exists, self.raw_earned)
+
+
+def expected_result_repr(self):
+    """
+    String representation for the ExpectedResult namedtuple which excludes
+    the "first_attempted" attribute that changes with each execution.  Needed
+    for consistency of ddt-generated test methods across pytest-xdist workers.
+    """
+    included = ('raw_earned', 'raw_possible', 'weighted_earned', 'weighted_possible', 'weight', 'graded')
+    attributes = ['{}={}'.format(name, getattr(self, name)) for name in included]
+    return '<ExpectedResult {}>'.format(' '.join(attributes))
+
+
 class TestScoredBlockTypes(TestCase):
     """
     Tests for the possibly_scored function.
@@ -52,13 +81,16 @@ class TestGetScore(TestCase):
     location = 'test_location'
 
     SubmissionValue = namedtuple('SubmissionValue', 'exists, points_earned, points_possible, created_at')
+    SubmissionValue.__repr__ = submission_value_repr
     CSMValue = namedtuple('CSMValue', 'exists, raw_earned, raw_possible, created')
+    CSMValue.__repr__ = csm_value_repr
     PersistedBlockValue = namedtuple('PersistedBlockValue', 'exists, raw_possible, weight, graded')
     ContentBlockValue = namedtuple('ContentBlockValue', 'raw_possible, weight, explicit_graded')
     ExpectedResult = namedtuple(
         'ExpectedResult',
         'raw_earned, raw_possible, weighted_earned, weighted_possible, weight, graded, first_attempted'
     )
+    ExpectedResult.__repr__ = expected_result_repr
 
     def _create_submissions_scores(self, submission_value):
         """
diff --git a/lms/djangoapps/grades/tests/test_signals.py b/lms/djangoapps/grades/tests/test_signals.py
index 58890f82764405aab0ab8aaba11bc920750225dd..f28282125d72c195f1f00842f12def4e3a93d1fb 100644
--- a/lms/djangoapps/grades/tests/test_signals.py
+++ b/lms/djangoapps/grades/tests/test_signals.py
@@ -16,7 +16,6 @@ from util.date_utils import to_timestamp
 from ..constants import ScoreDatabaseTableEnum
 from ..signals.handlers import (
     disconnect_submissions_signal_receiver,
-    enqueue_subsection_update,
     problem_raw_score_changed_handler,
     submissions_score_reset_handler,
     submissions_score_set_handler
@@ -28,20 +27,30 @@ UUID_REGEX = re.compile(ur'%(hex)s{8}-%(hex)s{4}-%(hex)s{4}-%(hex)s{4}-%(hex)s{1
 FROZEN_NOW_DATETIME = datetime.now().replace(tzinfo=pytz.UTC)
 FROZEN_NOW_TIMESTAMP = to_timestamp(FROZEN_NOW_DATETIME)
 
-SUBMISSION_SET_KWARGS = {
-    'points_possible': 10,
-    'points_earned': 5,
-    'anonymous_user_id': 'anonymous_id',
-    'course_id': 'CourseID',
-    'item_id': 'i4x://org/course/usage/123456',
-    'created_at': FROZEN_NOW_TIMESTAMP,
+SUBMISSIONS_SCORE_SET_HANDLER = 'submissions_score_set_handler'
+SUBMISSIONS_SCORE_RESET_HANDLER = 'submissions_score_reset_handler'
+HANDLERS = {
+    SUBMISSIONS_SCORE_SET_HANDLER: submissions_score_set_handler,
+    SUBMISSIONS_SCORE_RESET_HANDLER: submissions_score_reset_handler,
 }
 
-SUBMISSION_RESET_KWARGS = {
-    'anonymous_user_id': 'anonymous_id',
-    'course_id': 'CourseID',
-    'item_id': 'i4x://org/course/usage/123456',
-    'created_at': FROZEN_NOW_TIMESTAMP,
+SUBMISSION_SET_KWARGS = 'submission_set_kwargs'
+SUBMISSION_RESET_KWARGS = 'submission_reset_kwargs'
+SUBMISSION_KWARGS = {
+    SUBMISSION_SET_KWARGS: {
+        'points_possible': 10,
+        'points_earned': 5,
+        'anonymous_user_id': 'anonymous_id',
+        'course_id': 'CourseID',
+        'item_id': 'i4x://org/course/usage/123456',
+        'created_at': FROZEN_NOW_TIMESTAMP,
+    },
+    SUBMISSION_RESET_KWARGS: {
+        'anonymous_user_id': 'anonymous_id',
+        'course_id': 'CourseID',
+        'item_id': 'i4x://org/course/usage/123456',
+        'created_at': FROZEN_NOW_TIMESTAMP,
+    },
 }
 
 PROBLEM_RAW_SCORE_CHANGED_KWARGS = {
@@ -82,6 +91,10 @@ class ScoreChangedSignalRelayTest(TestCase):
     This ensures that listeners in the LMS only have to handle one type
     of signal for all scoring events regardless of their origin.
     """
+    SIGNALS = {
+        'score_set': score_set,
+        'score_reset': score_reset,
+    }
 
     def setUp(self):
         """
@@ -110,11 +123,11 @@ class ScoreChangedSignalRelayTest(TestCase):
         return mock
 
     @ddt.data(
-        [submissions_score_set_handler, SUBMISSION_SET_KWARGS, 5, 10],
-        [submissions_score_reset_handler, SUBMISSION_RESET_KWARGS, 0, 0],
+        [SUBMISSIONS_SCORE_SET_HANDLER, SUBMISSION_SET_KWARGS, 5, 10],
+        [SUBMISSIONS_SCORE_RESET_HANDLER, SUBMISSION_RESET_KWARGS, 0, 0],
     )
     @ddt.unpack
-    def test_score_set_signal_handler(self, handler, kwargs, earned, possible):
+    def test_score_set_signal_handler(self, handler_name, kwargs, earned, possible):
         """
         Ensure that on receipt of a score_(re)set signal from the Submissions API,
         the signal handler correctly converts it to a PROBLEM_WEIGHTED_SCORE_CHANGED
@@ -122,7 +135,9 @@ class ScoreChangedSignalRelayTest(TestCase):
 
         Also ensures that the handler calls user_by_anonymous_id correctly.
         """
-        handler(None, **kwargs)
+        local_kwargs = SUBMISSION_KWARGS[kwargs].copy()
+        handler = HANDLERS[handler_name]
+        handler(None, **local_kwargs)
         expected_set_kwargs = {
             'sender': None,
             'weighted_possible': possible,
@@ -134,35 +149,36 @@ class ScoreChangedSignalRelayTest(TestCase):
             'modified': FROZEN_NOW_TIMESTAMP,
             'score_db_table': 'submissions',
         }
-        if handler == submissions_score_reset_handler:
+        if kwargs == SUBMISSION_RESET_KWARGS:
             expected_set_kwargs['score_deleted'] = True
         self.signal_mock.assert_called_once_with(**expected_set_kwargs)
-        self.get_user_mock.assert_called_once_with(kwargs['anonymous_user_id'])
+        self.get_user_mock.assert_called_once_with(local_kwargs['anonymous_user_id'])
 
     def test_tnl_6599_zero_possible_bug(self):
         """
         Ensure that, if coming from the submissions API, signals indicating a
         a possible score of 0 are swallowed for reasons outlined in TNL-6559.
         """
-        local_kwargs = SUBMISSION_SET_KWARGS.copy()
+        local_kwargs = SUBMISSION_KWARGS[SUBMISSION_SET_KWARGS].copy()
         local_kwargs['points_earned'] = 0
         local_kwargs['points_possible'] = 0
         submissions_score_set_handler(None, **local_kwargs)
         self.signal_mock.assert_not_called()
 
     @ddt.data(
-        [submissions_score_set_handler, SUBMISSION_SET_KWARGS],
-        [submissions_score_reset_handler, SUBMISSION_RESET_KWARGS]
+        [SUBMISSIONS_SCORE_SET_HANDLER, SUBMISSION_SET_KWARGS],
+        [SUBMISSIONS_SCORE_RESET_HANDLER, SUBMISSION_RESET_KWARGS]
     )
     @ddt.unpack
-    def test_score_set_missing_kwarg(self, handler, kwargs):
+    def test_score_set_missing_kwarg(self, handler_name, kwargs):
         """
         Ensure that, on receipt of a score_(re)set signal from the Submissions API
         that does not have the correct kwargs, the courseware model does not
         generate a signal.
         """
-        for missing in kwargs:
-            local_kwargs = kwargs.copy()
+        handler = HANDLERS[handler_name]
+        for missing in SUBMISSION_KWARGS[kwargs]:
+            local_kwargs = SUBMISSION_KWARGS[kwargs].copy()
             del local_kwargs[missing]
 
             with self.assertRaises(KeyError):
@@ -170,18 +186,19 @@ class ScoreChangedSignalRelayTest(TestCase):
             self.signal_mock.assert_not_called()
 
     @ddt.data(
-        [submissions_score_set_handler, SUBMISSION_SET_KWARGS],
-        [submissions_score_reset_handler, SUBMISSION_RESET_KWARGS]
+        [SUBMISSIONS_SCORE_SET_HANDLER, SUBMISSION_SET_KWARGS],
+        [SUBMISSIONS_SCORE_RESET_HANDLER, SUBMISSION_RESET_KWARGS]
     )
     @ddt.unpack
-    def test_score_set_bad_user(self, handler, kwargs):
+    def test_score_set_bad_user(self, handler_name, kwargs):
         """
         Ensure that, on receipt of a score_(re)set signal from the Submissions API
         that has an invalid user ID, the courseware model does not generate a
         signal.
         """
+        handler = HANDLERS[handler_name]
         self.get_user_mock = self.setup_patch('lms.djangoapps.grades.signals.handlers.user_by_anonymous_id', None)
-        handler(None, **kwargs)
+        handler(None, **SUBMISSION_KWARGS[kwargs])
         self.signal_mock.assert_not_called()
 
     def test_raw_score_changed_signal_handler(self):
@@ -198,14 +215,18 @@ class ScoreChangedSignalRelayTest(TestCase):
         self.signal_mock.assert_called_with(**expected_set_kwargs)
 
     @ddt.data(
-        [score_set, 'lms.djangoapps.grades.signals.handlers.submissions_score_set_handler', SUBMISSION_SET_KWARGS],
-        [score_reset, 'lms.djangoapps.grades.signals.handlers.submissions_score_reset_handler', SUBMISSION_RESET_KWARGS]
+        ['score_set', 'lms.djangoapps.grades.signals.handlers.submissions_score_set_handler',
+         SUBMISSION_SET_KWARGS],
+        ['score_reset', 'lms.djangoapps.grades.signals.handlers.submissions_score_reset_handler',
+         SUBMISSION_RESET_KWARGS]
     )
     @ddt.unpack
-    def test_disconnect_manager(self, signal, handler, kwargs):
+    def test_disconnect_manager(self, signal_name, handler, kwargs):
         """
         Tests to confirm the disconnect_submissions_signal_receiver context manager is working correctly.
         """
+        signal = self.SIGNALS[signal_name]
+        kwargs = SUBMISSION_KWARGS[kwargs].copy()
         handler_mock = self.setup_patch(handler, None)
 
         # Receiver connected before we start
diff --git a/lms/djangoapps/instructor_task/tests/test_api.py b/lms/djangoapps/instructor_task/tests/test_api.py
index e010455ff56d41a63c3c70bd3ed73ff9ed903e70..95d9b6a84bf985f223bd6c0aafa23d1b6bee903e 100644
--- a/lms/djangoapps/instructor_task/tests/test_api.py
+++ b/lms/djangoapps/instructor_task/tests/test_api.py
@@ -7,6 +7,7 @@ from nose.plugins.attrib import attr
 
 from bulk_email.models import SEND_TO_LEARNERS, SEND_TO_MYSELF, SEND_TO_STAFF, CourseEmail
 from certificates.models import CertificateGenerationHistory, CertificateStatuses
+from common.test.utils import normalize_repr
 from courseware.tests.factories import UserFactory
 from lms.djangoapps.instructor_task.api import (
     SpecificStudentIdMissingError,
@@ -147,21 +148,29 @@ class InstructorTaskModuleSubmitTest(InstructorTaskModuleTestCase):
         self._test_submit_with_long_url(submit_delete_problem_state_for_all_students)
 
     @ddt.data(
-        (submit_rescore_problem_for_all_students, 'rescore_problem'),
-        (submit_rescore_problem_for_all_students, 'rescore_problem_if_higher', {'only_if_higher': True}),
-        (submit_rescore_problem_for_student, 'rescore_problem', {'student': True}),
-        (submit_rescore_problem_for_student, 'rescore_problem_if_higher', {'student': True, 'only_if_higher': True}),
-        (submit_reset_problem_attempts_for_all_students, 'reset_problem_attempts'),
-        (submit_delete_problem_state_for_all_students, 'delete_problem_state'),
-        (submit_rescore_entrance_exam_for_student, 'rescore_problem', {'student': True}),
+        (normalize_repr(submit_rescore_problem_for_all_students), 'rescore_problem'),
         (
-            submit_rescore_entrance_exam_for_student,
+            normalize_repr(submit_rescore_problem_for_all_students),
+            'rescore_problem_if_higher',
+            {'only_if_higher': True}
+        ),
+        (normalize_repr(submit_rescore_problem_for_student), 'rescore_problem', {'student': True}),
+        (
+            normalize_repr(submit_rescore_problem_for_student),
+            'rescore_problem_if_higher',
+            {'student': True, 'only_if_higher': True}
+        ),
+        (normalize_repr(submit_reset_problem_attempts_for_all_students), 'reset_problem_attempts'),
+        (normalize_repr(submit_delete_problem_state_for_all_students), 'delete_problem_state'),
+        (normalize_repr(submit_rescore_entrance_exam_for_student), 'rescore_problem', {'student': True}),
+        (
+            normalize_repr(submit_rescore_entrance_exam_for_student),
             'rescore_problem_if_higher',
             {'student': True, 'only_if_higher': True},
         ),
-        (submit_reset_problem_attempts_in_entrance_exam, 'reset_problem_attempts', {'student': True}),
-        (submit_delete_entrance_exam_state_for_student, 'delete_problem_state', {'student': True}),
-        (submit_override_score, 'override_problem_score', {'student': True, 'score': 0})
+        (normalize_repr(submit_reset_problem_attempts_in_entrance_exam), 'reset_problem_attempts', {'student': True}),
+        (normalize_repr(submit_delete_entrance_exam_state_for_student), 'delete_problem_state', {'student': True}),
+        (normalize_repr(submit_override_score), 'override_problem_score', {'student': True, 'score': 0})
     )
     @ddt.unpack
     def test_submit_task(self, task_function, expected_task_type, params=None):
diff --git a/lms/djangoapps/mobile_api/users/tests.py b/lms/djangoapps/mobile_api/users/tests.py
index d9167318f07f9cc3532a96315097d0791e95bc1a..88b50fd63ee07796d64ddbb6ca4e82d915941117 100644
--- a/lms/djangoapps/mobile_api/users/tests.py
+++ b/lms/djangoapps/mobile_api/users/tests.py
@@ -84,6 +84,11 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
     LAST_WEEK = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=7)
     ADVERTISED_START = "Spring 2016"
     ENABLED_SIGNALS = ['course_published']
+    DATES = {
+        'next_week': NEXT_WEEK,
+        'last_week': LAST_WEEK,
+        'default_start_date': DEFAULT_START_DATE,
+    }
 
     @patch.dict(settings.FEATURES, {"ENABLE_DISCUSSION_SERVICE": True})
     def setUp(self, *args, **kwargs):
@@ -175,12 +180,12 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
                 self.assertFalse(result['has_access'])
 
     @ddt.data(
-        (NEXT_WEEK, ADVERTISED_START, ADVERTISED_START, "string"),
-        (NEXT_WEEK, None, defaultfilters.date(NEXT_WEEK, "DATE_FORMAT"), "timestamp"),
-        (NEXT_WEEK, '', defaultfilters.date(NEXT_WEEK, "DATE_FORMAT"), "timestamp"),
-        (DEFAULT_START_DATE, ADVERTISED_START, ADVERTISED_START, "string"),
-        (DEFAULT_START_DATE, '', None, "empty"),
-        (DEFAULT_START_DATE, None, None, "empty"),
+        ('next_week', ADVERTISED_START, ADVERTISED_START, "string"),
+        ('next_week', None, defaultfilters.date(NEXT_WEEK, "DATE_FORMAT"), "timestamp"),
+        ('next_week', '', defaultfilters.date(NEXT_WEEK, "DATE_FORMAT"), "timestamp"),
+        ('default_start_date', ADVERTISED_START, ADVERTISED_START, "string"),
+        ('default_start_date', '', None, "empty"),
+        ('default_start_date', None, None, "empty"),
     )
     @ddt.unpack
     @patch.dict(settings.FEATURES, {'DISABLE_START_DATES': False, 'ENABLE_MKTG_SITE': True})
@@ -190,7 +195,7 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
         case the course has not started
         """
         self.login()
-        course = CourseFactory.create(start=start, advertised_start=advertised_start, mobile_available=True)
+        course = CourseFactory.create(start=self.DATES[start], advertised_start=advertised_start, mobile_available=True)
         self.enroll(course.id)
 
         response = self.api_response()
diff --git a/lms/djangoapps/teams/tests/test_models.py b/lms/djangoapps/teams/tests/test_models.py
index 7bcd29d1445fa781ad37e6afbbdc9442c4f19926..31499e23d1a0d57389a1d176cf6b5771e8ee2e11 100644
--- a/lms/djangoapps/teams/tests/test_models.py
+++ b/lms/djangoapps/teams/tests/test_models.py
@@ -118,17 +118,17 @@ class TeamMembershipTest(SharedModuleStoreTestCase):
 class TeamSignalsTest(EventTestMixin, SharedModuleStoreTestCase):
     """Tests for handling of team-related signals."""
 
-    SIGNALS_LIST = (
-        thread_created,
-        thread_edited,
-        thread_deleted,
-        thread_voted,
-        comment_created,
-        comment_edited,
-        comment_deleted,
-        comment_voted,
-        comment_endorsed
-    )
+    SIGNALS = {
+        'thread_created': thread_created,
+        'thread_edited': thread_edited,
+        'thread_deleted': thread_deleted,
+        'thread_voted': thread_voted,
+        'comment_created': comment_created,
+        'comment_edited': comment_edited,
+        'comment_deleted': comment_deleted,
+        'comment_voted': comment_voted,
+        'comment_endorsed': comment_endorsed,
+    }
 
     DISCUSSION_TOPIC_ID = 'test_topic'
 
@@ -180,30 +180,33 @@ class TeamSignalsTest(EventTestMixin, SharedModuleStoreTestCase):
 
     @ddt.data(
         *itertools.product(
-            SIGNALS_LIST,
+            SIGNALS.keys(),
             (('user', True), ('moderator', False))
         )
     )
     @ddt.unpack
-    def test_signals(self, signal, (user, should_update)):
+    def test_signals(self, signal_name, (user, should_update)):
         """Test that `last_activity_at` is correctly updated when team-related
         signals are sent.
         """
         with self.assert_last_activity_updated(should_update):
             user = getattr(self, user)
+            signal = self.SIGNALS[signal_name]
             signal.send(sender=None, user=user, post=self.mock_comment())
 
-    @ddt.data(thread_voted, comment_voted)
-    def test_vote_others_post(self, signal):
+    @ddt.data('thread_voted', 'comment_voted')
+    def test_vote_others_post(self, signal_name):
         """Test that voting on another user's post correctly fires a
         signal."""
         with self.assert_last_activity_updated(True):
+            signal = self.SIGNALS[signal_name]
             signal.send(sender=None, user=self.user, post=self.mock_comment(user=self.moderator))
 
-    @ddt.data(*SIGNALS_LIST)
-    def test_signals_course_context(self, signal):
+    @ddt.data(*SIGNALS.keys())
+    def test_signals_course_context(self, signal_name):
         """Test that `last_activity_at` is not updated when activity takes
         place in discussions outside of a team.
         """
         with self.assert_last_activity_updated(False):
+            signal = self.SIGNALS[signal_name]
             signal.send(sender=None, user=self.user, post=self.mock_comment(context='course'))
diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py
index c72c4de51878408265cd5f8c27bb15e90fb03224..3ff2b7b45873a9371ca274e92cb007bc7eb74e75 100644
--- a/lms/djangoapps/verify_student/tests/test_views.py
+++ b/lms/djangoapps/verify_student/tests/test_views.py
@@ -89,8 +89,15 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
     PASSWORD = "test_password"
 
     NOW = datetime.now(pytz.UTC)
-    YESTERDAY = NOW - timedelta(days=1)
-    TOMORROW = NOW + timedelta(days=1)
+    YESTERDAY = 'yesterday'
+    TOMORROW = 'tomorrow'
+    NEXT_YEAR = 'next_year'
+    DATES = {
+        YESTERDAY: NOW - timedelta(days=1),
+        TOMORROW: NOW + timedelta(days=1),
+        NEXT_YEAR: NOW + timedelta(days=360),
+        None: None,
+    }
 
     URLCONF_MODULES = ['openedx.core.djangoapps.embargo']
 
@@ -492,7 +499,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
     )
     @ddt.unpack
     def test_payment_confirmation_course_details(self, course_start, show_courseware_url):
-        course = self._create_course("verified", course_start=course_start)
+        course = self._create_course("verified", course_start=self.DATES[course_start])
         self._enroll(course.id, "verified")
         response = self._get_page('verify_student_payment_confirmation', course.id)
 
@@ -753,9 +760,10 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
         self.assertContains(response, "verification deadline")
         self.assertContains(response, deadline)
 
-    @ddt.data(datetime.now(tz=pytz.UTC) + timedelta(days=360), None)
+    @ddt.data(NEXT_YEAR, None)
     def test_course_mode_expired_verification_deadline_in_future(self, verification_deadline):
         """Verify that student can not upgrade in expired course mode."""
+        verification_deadline = self.DATES[verification_deadline]
         course_modes = ("verified", "credit")
         course = self._create_course(*course_modes)
 
diff --git a/openedx/core/djangoapps/content/block_structure/tests/helpers.py b/openedx/core/djangoapps/content/block_structure/tests/helpers.py
index 601815721398a758c827ee2885fcc10d6cd8347d..83e5b2d666f869c2d2c1b19382bf0df48cba1553 100644
--- a/openedx/core/djangoapps/content/block_structure/tests/helpers.py
+++ b/openedx/core/djangoapps/content/block_structure/tests/helpers.py
@@ -183,6 +183,9 @@ class MockTransformer(BlockStructureTransformer):
     def transform(self, usage_info, block_structure):
         pass
 
+    def __repr__(self):
+        return self.name()
+
 
 class MockFilteringTransformer(FilteringTransformerMixin, BlockStructureTransformer):
     """
diff --git a/openedx/core/djangoapps/content/course_overviews/tests/test_course_overviews.py b/openedx/core/djangoapps/content/course_overviews/tests/test_course_overviews.py
index 5558d7e4aa9bec206720cebba0a66286ca7c7f35..b94458e04d61466713791723b754bad456263846 100644
--- a/openedx/core/djangoapps/content/course_overviews/tests/test_course_overviews.py
+++ b/openedx/core/djangoapps/content/course_overviews/tests/test_course_overviews.py
@@ -46,10 +46,18 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
     """
 
     TODAY = timezone.now()
-    LAST_MONTH = TODAY - datetime.timedelta(days=30)
-    LAST_WEEK = TODAY - datetime.timedelta(days=7)
-    NEXT_WEEK = TODAY + datetime.timedelta(days=7)
-    NEXT_MONTH = TODAY + datetime.timedelta(days=30)
+    LAST_MONTH = 'last_month'
+    LAST_WEEK = 'last_week'
+    NEXT_WEEK = 'next_week'
+    NEXT_MONTH = 'next_month'
+    DATES = {
+        'default_start_date': DEFAULT_START_DATE,
+        LAST_MONTH: TODAY - datetime.timedelta(days=30),
+        LAST_WEEK: TODAY - datetime.timedelta(days=7),
+        NEXT_WEEK: TODAY + datetime.timedelta(days=7),
+        NEXT_MONTH: TODAY + datetime.timedelta(days=30),
+        None: None,
+    }
 
     COURSE_OVERVIEW_TABS = {'courseware', 'info', 'textbooks', 'discussion', 'wiki', 'progress'}
 
@@ -229,7 +237,7 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
             },
             {
                 #                                           # Don't set display name
-                "start": DEFAULT_START_DATE,                # Default start and end dates
+                "start": 'default_start_date',              # Default start and end dates
                 "end": None,
                 "advertised_start": None,                   # No advertised start
                 "pre_requisite_courses": [],                # No pre-requisites
@@ -251,10 +259,15 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
             modulestore_type (ModuleStoreEnum.Type): type of store to create the
                 course in.
         """
+        kwargs = course_kwargs.copy()
+        kwargs['start'] = self.DATES[course_kwargs['start']]
+        kwargs['end'] = self.DATES[course_kwargs['end']]
+        if 'announcement' in course_kwargs:
+            kwargs['announcement'] = self.DATES[course_kwargs['announcement']]
         # Note: We specify a value for 'run' here because, for some reason,
         # .create raises an InvalidKeyError if we don't (even though my
         # other test functions don't specify a run but work fine).
-        course = CourseFactory.create(default_store=modulestore_type, run="TestRun", **course_kwargs)
+        course = CourseFactory.create(default_store=modulestore_type, run="TestRun", **kwargs)
         self.check_course_overview_against_course(course)
 
     @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
diff --git a/openedx/core/djangoapps/credit/tests/test_signals.py b/openedx/core/djangoapps/credit/tests/test_signals.py
index 50566faf3c5f72d45277ed6d85f93aaf4ee9c6ed..ead33b557493214455e50ffe9cba2632b22baf03 100644
--- a/openedx/core/djangoapps/credit/tests/test_signals.py
+++ b/openedx/core/djangoapps/credit/tests/test_signals.py
@@ -35,6 +35,12 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
     VALID_DUE_DATE = datetime.now(pytz.UTC) + timedelta(days=20)
     EXPIRED_DUE_DATE = datetime.now(pytz.UTC) - timedelta(days=20)
 
+    DATES = {
+        'valid': VALID_DUE_DATE,
+        'expired': EXPIRED_DUE_DATE,
+        None: None,
+    }
+
     def setUp(self):
         super(TestMinGradedRequirementStatus, self).setUp()
         self.course = CourseFactory.create(
@@ -85,13 +91,13 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
             self.assertEqual(req_status[0]['reason'], expected_reason)
 
     @ddt.data(
-        (0.6, VALID_DUE_DATE),
+        (0.6, 'valid'),
         (0.52, None),
     )
     @ddt.unpack
-    def test_min_grade_requirement_with_valid_grade(self, grade, due_date):
+    def test_min_grade_requirement_with_valid_grade(self, grade, due_date_name):
         """Test with valid grades submitted before deadline"""
-        self.assert_requirement_status(grade, due_date, 'satisfied')
+        self.assert_requirement_status(grade, self.DATES[due_date_name], 'satisfied')
 
     def test_grade_changed(self):
         """ Verify successive calls to update a satisfied grade requirement are recorded. """
@@ -106,12 +112,12 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
     @ddt.data(
         (0.50, None),
         (0.51, None),
-        (0.40, VALID_DUE_DATE),
+        (0.40, 'valid'),
     )
     @ddt.unpack
-    def test_min_grade_requirement_failed_grade_valid_deadline(self, grade, due_date):
+    def test_min_grade_requirement_failed_grade_valid_deadline(self, grade, due_date_name):
         """Test with failed grades and deadline is still open or not defined."""
-        self.assert_requirement_status(grade, due_date, None)
+        self.assert_requirement_status(grade, self.DATES[due_date_name], None)
 
     def test_min_grade_requirement_failed_grade_expired_deadline(self):
         """Test with failed grades and deadline expire"""
diff --git a/openedx/core/djangoapps/programs/tests/test_backpopulate_program_credentials.py b/openedx/core/djangoapps/programs/tests/test_backpopulate_program_credentials.py
index 09d73029fd7a75d3b4f2bcc39b2f8319963b3296..006fb3d359048c5b78d94f38337fa0510258eaee 100644
--- a/openedx/core/djangoapps/programs/tests/test_backpopulate_program_credentials.py
+++ b/openedx/core/djangoapps/programs/tests/test_backpopulate_program_credentials.py
@@ -29,6 +29,10 @@ COMMAND_MODULE = 'openedx.core.djangoapps.programs.management.commands.backpopul
 class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsApiConfigMixin, TestCase):
     """Tests for the backpopulate_program_credentials management command."""
     course_run_key, alternate_course_run_key = (generate_course_run_key() for __ in range(2))
+    # Constants for the _get_programs_data hierarchy types used in test_flatten()
+    SEPARATE_PROGRAMS = 'separate_programs'
+    SEPARATE_COURSES = 'separate_courses'
+    SAME_COURSE = 'same_course'
 
     def setUp(self):
         super(BackpopulateProgramCredentialsTests, self).setUp()
@@ -44,6 +48,54 @@ class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsAp
         catalog_integration = self.create_catalog_integration()
         UserFactory(username=catalog_integration.service_username)
 
+    def _get_programs_data(self, hierarchy_type):
+        """
+        Generate a mock response for get_programs() with the given type of
+        course hierarchy.  Dramatically simplifies (and makes consistent
+        between test runs) the ddt-generated test_flatten methods.
+        """
+        if hierarchy_type == self.SEPARATE_PROGRAMS:
+            return [
+                ProgramFactory(
+                    courses=[
+                        CourseFactory(course_runs=[
+                            CourseRunFactory(key=self.course_run_key),
+                        ]),
+                    ]
+                ),
+                ProgramFactory(
+                    courses=[
+                        CourseFactory(course_runs=[
+                            CourseRunFactory(key=self.alternate_course_run_key),
+                        ]),
+                    ]
+                ),
+            ]
+        elif hierarchy_type == self.SEPARATE_COURSES:
+            return [
+                ProgramFactory(
+                    courses=[
+                        CourseFactory(course_runs=[
+                            CourseRunFactory(key=self.course_run_key),
+                        ]),
+                        CourseFactory(course_runs=[
+                            CourseRunFactory(key=self.alternate_course_run_key),
+                        ]),
+                    ]
+                ),
+            ]
+        else:  # SAME_COURSE
+            return [
+                ProgramFactory(
+                    courses=[
+                        CourseFactory(course_runs=[
+                            CourseRunFactory(key=self.course_run_key),
+                            CourseRunFactory(key=self.alternate_course_run_key),
+                        ]),
+                    ]
+                ),
+            ]
+
     @ddt.data(True, False)
     def test_handle(self, commit, mock_task, mock_get_programs):
         """
@@ -112,49 +164,10 @@ class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsAp
         # The task should be called for both users since professional and no-id-professional are equivalent.
         mock_task.assert_has_calls([mock.call(self.alice.username), mock.call(self.bob.username)])
 
-    @ddt.data(
-        [
-            ProgramFactory(
-                courses=[
-                    CourseFactory(course_runs=[
-                        CourseRunFactory(key=course_run_key),
-                    ]),
-                ]
-            ),
-            ProgramFactory(
-                courses=[
-                    CourseFactory(course_runs=[
-                        CourseRunFactory(key=alternate_course_run_key),
-                    ]),
-                ]
-            ),
-        ],
-        [
-            ProgramFactory(
-                courses=[
-                    CourseFactory(course_runs=[
-                        CourseRunFactory(key=course_run_key),
-                    ]),
-                    CourseFactory(course_runs=[
-                        CourseRunFactory(key=alternate_course_run_key),
-                    ]),
-                ]
-            ),
-        ],
-        [
-            ProgramFactory(
-                courses=[
-                    CourseFactory(course_runs=[
-                        CourseRunFactory(key=course_run_key),
-                        CourseRunFactory(key=alternate_course_run_key),
-                    ]),
-                ]
-            ),
-        ],
-    )
-    def test_handle_flatten(self, data, mock_task, mock_get_programs):
+    @ddt.data(SEPARATE_PROGRAMS, SEPARATE_COURSES, SAME_COURSE)
+    def test_handle_flatten(self, hierarchy_type, mock_task, mock_get_programs):
         """Verify that program structures are flattened correctly."""
-        mock_get_programs.return_value = data
+        mock_get_programs.return_value = self._get_programs_data(hierarchy_type)
 
         GeneratedCertificateFactory(
             user=self.alice,
diff --git a/openedx/core/djangoapps/user_api/validation/tests/test_views.py b/openedx/core/djangoapps/user_api/validation/tests/test_views.py
index 5ff294a0413f4c3c1f9f99415f354802c998dd00..4796cc8f60ab26f6d6dbc4b2ab65e48b841404f6 100644
--- a/openedx/core/djangoapps/user_api/validation/tests/test_views.py
+++ b/openedx/core/djangoapps/user_api/validation/tests/test_views.py
@@ -53,11 +53,11 @@ class RegistrationValidationViewTests(test_utils.ApiTestCase):
         )
 
     @ddt.data(
-        ['name', (name for name in testutils.VALID_NAMES)],
-        ['email', (email for email in testutils.VALID_EMAILS)],
-        ['password', (password for password in testutils.VALID_PASSWORDS)],
-        ['username', (username for username in testutils.VALID_USERNAMES)],
-        ['country', (country for country in testutils.VALID_COUNTRIES)]
+        ['name', [name for name in testutils.VALID_NAMES]],
+        ['email', [email for email in testutils.VALID_EMAILS]],
+        ['password', [password for password in testutils.VALID_PASSWORDS]],
+        ['username', [username for username in testutils.VALID_USERNAMES]],
+        ['country', [country for country in testutils.VALID_COUNTRIES]]
     )
     @ddt.unpack
     def test_positive_validation_decision(self, form_field_name, user_data):
@@ -71,11 +71,11 @@ class RegistrationValidationViewTests(test_utils.ApiTestCase):
 
     @ddt.data(
         # Skip None type for invalidity checks.
-        ['name', (name for name in testutils.INVALID_NAMES[1:])],
-        ['email', (email for email in testutils.INVALID_EMAILS[1:])],
-        ['password', (password for password in testutils.INVALID_PASSWORDS[1:])],
-        ['username', (username for username in testutils.INVALID_USERNAMES[1:])],
-        ['country', (country for country in testutils.INVALID_COUNTRIES[1:])]
+        ['name', [name for name in testutils.INVALID_NAMES[1:]]],
+        ['email', [email for email in testutils.INVALID_EMAILS[1:]]],
+        ['password', [password for password in testutils.INVALID_PASSWORDS[1:]]],
+        ['username', [username for username in testutils.INVALID_USERNAMES[1:]]],
+        ['country', [country for country in testutils.INVALID_COUNTRIES[1:]]]
     )
     @ddt.unpack
     def test_negative_validation_decision(self, form_field_name, user_data):
diff --git a/openedx/core/djangoapps/util/tests/test_user_messages.py b/openedx/core/djangoapps/util/tests/test_user_messages.py
index 0f40f6b2412999893a7b6ff058e5869dbe158cd9..b94c0d7c6523f3196bb9d813d36d089d986c337e 100644
--- a/openedx/core/djangoapps/util/tests/test_user_messages.py
+++ b/openedx/core/djangoapps/util/tests/test_user_messages.py
@@ -6,6 +6,8 @@ import ddt
 
 from django.contrib.messages.middleware import MessageMiddleware
 from django.test import RequestFactory, TestCase
+
+from common.test.utils import normalize_repr
 from openedx.core.djangolib.markup import HTML, Text
 from student.tests.factories import UserFactory
 
@@ -60,10 +62,10 @@ class UserMessagesTestCase(TestCase):
         self.assertEquals(messages[0].icon_class, expected_icon_class)
 
     @ddt.data(
-        (PageLevelMessages.register_error_message, UserMessageType.ERROR),
-        (PageLevelMessages.register_info_message, UserMessageType.INFO),
-        (PageLevelMessages.register_success_message, UserMessageType.SUCCESS),
-        (PageLevelMessages.register_warning_message, UserMessageType.WARNING),
+        (normalize_repr(PageLevelMessages.register_error_message), UserMessageType.ERROR),
+        (normalize_repr(PageLevelMessages.register_info_message), UserMessageType.INFO),
+        (normalize_repr(PageLevelMessages.register_success_message), UserMessageType.SUCCESS),
+        (normalize_repr(PageLevelMessages.register_warning_message), UserMessageType.WARNING),
     )
     @ddt.unpack
     def test_message_type(self, register_message_function, expected_message_type):
diff --git a/openedx/core/lib/xblock_builtin/xblock_discussion/tests.py b/openedx/core/lib/xblock_builtin/xblock_discussion/tests.py
index b02e08904129f40045b5c53ff60519df39ae7652..4640109df23070a0ed806bec42ff4a6adc6be8e6 100644
--- a/openedx/core/lib/xblock_builtin/xblock_discussion/tests.py
+++ b/openedx/core/lib/xblock_builtin/xblock_discussion/tests.py
@@ -16,7 +16,16 @@ from xblock.fields import ScopeIds, UNIQUE_ID, NO_CACHE_VALUE
 from xblock.runtime import Runtime
 
 
+def attribute_pair_repr(self):
+    """
+    Custom string representation for the AttributePair namedtuple which is
+    consistent between test runs.
+    """
+    return '<AttributePair name={}>'.format(self.name)
+
+
 AttributePair = namedtuple("AttributePair", ["name", "value"])
+AttributePair.__repr__ = attribute_pair_repr
 
 
 ID_ATTR_NAMES = ("discussion_id", "id",)
diff --git a/openedx/features/course_experience/tests/views/test_course_outline.py b/openedx/features/course_experience/tests/views/test_course_outline.py
index 2ba7122648940e5c8ff7c7a3fda5937fd451000b..70d03609d1be4c79b6db4d742d450e7ff7bc3e67 100644
--- a/openedx/features/course_experience/tests/views/test_course_outline.py
+++ b/openedx/features/course_experience/tests/views/test_course_outline.py
@@ -20,8 +20,6 @@ from xmodule.course_module import DEFAULT_START_DATE
 from .test_course_home import course_home_url
 
 TEST_PASSWORD = 'test'
-FUTURE_DAY = datetime.datetime.now() + datetime.timedelta(days=30)
-PAST_DAY = datetime.datetime.now() - datetime.timedelta(days=30)
 
 
 class TestCourseOutlinePage(SharedModuleStoreTestCase):
@@ -343,14 +341,22 @@ class TestEmptyCourseOutlinePage(SharedModuleStoreTestCase):
     """
     Test the new course outline view.
     """
+    FUTURE_DAY = 'future_day'
+    PAST_DAY = 'past_day'
+    DATES = {
+        'default_start_date': DEFAULT_START_DATE,
+        FUTURE_DAY: datetime.datetime.now() + datetime.timedelta(days=30),
+        PAST_DAY: datetime.datetime.now() - datetime.timedelta(days=30),
+    }
+
     @ddt.data(
         (FUTURE_DAY, 'This course has not started yet, and will launch on'),
         (PAST_DAY, "We're still working on course content."),
-        (DEFAULT_START_DATE, 'This course has not started yet.'),
+        ('default_start_date', 'This course has not started yet.'),
     )
     @ddt.unpack
-    def test_empty_course_rendering(self, start_date, expected_text):
-        course = CourseFactory.create(start=start_date)
+    def test_empty_course_rendering(self, start_date_name, expected_text):
+        course = CourseFactory.create(start=self.DATES[start_date_name])
         test_user = UserFactory(password=TEST_PASSWORD)
         CourseEnrollment.enroll(test_user, course.id)
         self.client.login(username=test_user.username, password=TEST_PASSWORD)