From 4e8668d3be662fe229790c91bfc575da2837bd9e Mon Sep 17 00:00:00 2001
From: Jeremy Bowman <jbowman@edx.org>
Date: Wed, 1 Aug 2018 13:54:06 -0400
Subject: [PATCH] TE-2524 Stop using nose.plugins - LMS 2

---
 .../blocks/transformers/tests/test_milestones.py           | 3 +--
 lms/djangoapps/course_api/tests/test_serializers.py        | 3 +--
 lms/djangoapps/course_api/tests/test_views.py              | 2 +-
 .../transformers/tests/test_hidden_content.py              | 3 +--
 .../course_blocks/transformers/tests/test_split_test.py    | 3 +--
 .../course_blocks/transformers/tests/test_start_date.py    | 3 +--
 .../transformers/tests/test_user_partitions.py             | 7 ++++---
 .../course_blocks/transformers/tests/test_visibility.py    | 3 +--
 lms/djangoapps/course_wiki/tests/test_access.py            | 2 +-
 .../course_wiki/tests/test_comprehensive_theming.py        | 3 +--
 lms/djangoapps/course_wiki/tests/test_middleware.py        | 3 +--
 lms/djangoapps/course_wiki/tests/tests.py                  | 3 +--
 .../management/commands/tests/test_dump_course.py          | 3 +--
 lms/djangoapps/courseware/tests/test_about.py              | 2 +-
 lms/djangoapps/courseware/tests/test_access.py             | 2 +-
 lms/djangoapps/courseware/tests/test_course_info.py        | 2 +-
 lms/djangoapps/courseware/tests/test_course_survey.py      | 4 +---
 lms/djangoapps/courseware/tests/test_course_tools.py       | 3 +--
 lms/djangoapps/courseware/tests/test_courses.py            | 2 +-
 lms/djangoapps/courseware/tests/test_date_summary.py       | 2 +-
 lms/djangoapps/courseware/tests/test_draft_modulestore.py  | 4 ++--
 lms/djangoapps/courseware/tests/test_entrance_exam.py      | 4 ++--
 lms/djangoapps/courseware/tests/test_favicon.py            | 3 +--
 lms/djangoapps/courseware/tests/test_field_overrides.py    | 2 +-
 lms/djangoapps/courseware/tests/test_footer.py             | 3 +--
 lms/djangoapps/courseware/tests/test_group_access.py       | 4 ++--
 lms/djangoapps/courseware/tests/test_i18n.py               | 2 +-
 lms/djangoapps/courseware/tests/test_lti_integration.py    | 6 ++----
 lms/djangoapps/courseware/tests/test_masquerade.py         | 2 +-
 lms/djangoapps/courseware/tests/test_microsites.py         | 5 +----
 lms/djangoapps/courseware/tests/test_middleware.py         | 3 +--
 lms/djangoapps/courseware/tests/test_model_data.py         | 2 +-
 lms/djangoapps/courseware/tests/test_module_render.py      | 3 +--
 lms/djangoapps/courseware/tests/test_navigation.py         | 4 +---
 lms/djangoapps/courseware/tests/test_password_history.py   | 3 +--
 lms/djangoapps/courseware/tests/test_split_module.py       | 6 +++---
 .../courseware/tests/test_submitting_problems.py           | 2 +-
 lms/djangoapps/courseware/tests/test_tabs.py               | 3 +--
 lms/djangoapps/courseware/tests/test_video_handlers.py     | 2 +-
 lms/djangoapps/courseware/tests/test_video_mongo.py        | 2 +-
 lms/djangoapps/courseware/tests/test_video_xml.py          | 4 +---
 .../courseware/tests/test_view_authentication.py           | 6 +++---
 lms/djangoapps/courseware/tests/test_views.py              | 2 +-
 lms/djangoapps/courseware/tests/test_word_cloud.py         | 4 +---
 lms/djangoapps/courseware/tests/tests.py                   | 2 +-
 .../management/commands/tests/test_git_add_course.py       | 4 +---
 lms/djangoapps/dashboard/tests/test_sysadmin.py            | 3 +--
 lms/djangoapps/instructor/tests/test_access.py             | 2 +-
 lms/djangoapps/instructor/tests/test_api.py                | 2 +-
 .../instructor/tests/test_api_email_localization.py        | 4 ++--
 lms/djangoapps/instructor/tests/test_certificates.py       | 2 +-
 lms/djangoapps/instructor/tests/test_ecommerce.py          | 4 ++--
 lms/djangoapps/instructor/tests/test_email.py              | 6 +++---
 lms/djangoapps/instructor/tests/test_enrollment.py         | 2 +-
 lms/djangoapps/instructor/tests/test_proctoring.py         | 4 ++--
 lms/djangoapps/instructor/tests/test_registration_codes.py | 4 ++--
 lms/djangoapps/instructor/tests/test_services.py           | 4 ++--
 lms/djangoapps/instructor/tests/test_spoc_gradebook.py     | 2 +-
 lms/djangoapps/instructor/tests/test_tools.py              | 2 +-
 .../instructor/tests/views/test_instructor_dashboard.py    | 3 +--
 lms/djangoapps/lti_provider/tests/test_views.py            | 4 +---
 lms/djangoapps/mobile_api/course_info/tests.py             | 5 ++---
 lms/djangoapps/mobile_api/users/tests.py                   | 2 +-
 lms/djangoapps/mobile_api/video_outlines/tests.py          | 2 +-
 lms/djangoapps/shoppingcart/tests/test_models.py           | 6 +++---
 lms/djangoapps/shoppingcart/tests/test_views.py            | 3 +--
 lms/djangoapps/student_account/test/test_views.py          | 5 +----
 lms/djangoapps/support/tests/test_views.py                 | 4 +---
 lms/djangoapps/teams/tests/test_views.py                   | 2 --
 lms/djangoapps/verify_student/tests/test_views.py          | 2 +-
 scripts/thresholds.sh                                      | 2 +-
 71 files changed, 91 insertions(+), 135 deletions(-)

diff --git a/lms/djangoapps/course_api/blocks/transformers/tests/test_milestones.py b/lms/djangoapps/course_api/blocks/transformers/tests/test_milestones.py
index 7366cd36091..115e69fcde6 100644
--- a/lms/djangoapps/course_api/blocks/transformers/tests/test_milestones.py
+++ b/lms/djangoapps/course_api/blocks/transformers/tests/test_milestones.py
@@ -4,7 +4,6 @@ Tests for ProctoredExamTransformer.
 import ddt
 from milestones.tests.utils import MilestonesTestCaseMixin
 from mock import Mock, patch
-from nose.plugins.attrib import attr
 
 from gating import api as lms_gating_api
 from lms.djangoapps.course_blocks.api import get_course_blocks
@@ -16,7 +15,6 @@ from student.tests.factories import CourseEnrollmentFactory
 from ..milestones import MilestonesAndSpecialExamsTransformer
 
 
-@attr(shard=3)
 @ddt.ddt
 @patch.dict('django.conf.settings.FEATURES', {'ENABLE_SPECIAL_EXAMS': True})
 class MilestonesTransformerTestCase(CourseStructureTestCase, MilestonesTestCaseMixin):
@@ -24,6 +22,7 @@ class MilestonesTransformerTestCase(CourseStructureTestCase, MilestonesTestCaseM
     Test behavior of ProctoredExamTransformer
     """
     TRANSFORMER_CLASS_TO_TEST = MilestonesAndSpecialExamsTransformer
+    shard = 3
 
     def setUp(self):
         """
diff --git a/lms/djangoapps/course_api/tests/test_serializers.py b/lms/djangoapps/course_api/tests/test_serializers.py
index 179df69e9bb..dac4f2bab52 100644
--- a/lms/djangoapps/course_api/tests/test_serializers.py
+++ b/lms/djangoapps/course_api/tests/test_serializers.py
@@ -7,7 +7,6 @@ from __future__ import unicode_literals
 from datetime import datetime
 
 import ddt
-from nose.plugins.attrib import attr
 from rest_framework.request import Request
 from rest_framework.test import APIRequestFactory
 from xblock.core import XBlock
@@ -22,7 +21,6 @@ from ..serializers import CourseDetailSerializer, CourseSerializer
 from .mixins import CourseApiFactoryMixin
 
 
-@attr(shard=3)
 @ddt.ddt
 class TestCourseSerializer(CourseApiFactoryMixin, ModuleStoreTestCase):
     """
@@ -31,6 +29,7 @@ class TestCourseSerializer(CourseApiFactoryMixin, ModuleStoreTestCase):
     expected_mongo_calls = 0
     maxDiff = 5000  # long enough to show mismatched dicts, in case of error
     serializer_class = CourseSerializer
+    shard = 3
 
     ENABLED_SIGNALS = ['course_published']
 
diff --git a/lms/djangoapps/course_api/tests/test_views.py b/lms/djangoapps/course_api/tests/test_views.py
index 2f545521caa..26d74e02b4a 100644
--- a/lms/djangoapps/course_api/tests/test_views.py
+++ b/lms/djangoapps/course_api/tests/test_views.py
@@ -8,11 +8,11 @@ from django.core.exceptions import ImproperlyConfigured
 from django.urls import reverse
 from django.test import RequestFactory
 from django.test.utils import override_settings
-from nose.plugins.attrib import attr
 from search.tests.test_course_discovery import DemoCourse
 from search.tests.tests import TEST_INDEX_NAME
 from search.tests.utils import SearcherMixin
 
+from openedx.core.lib.tests import attr
 from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
 from waffle.testutils import override_switch
 
diff --git a/lms/djangoapps/course_blocks/transformers/tests/test_hidden_content.py b/lms/djangoapps/course_blocks/transformers/tests/test_hidden_content.py
index 86032ec3684..f90fb01cba2 100644
--- a/lms/djangoapps/course_blocks/transformers/tests/test_hidden_content.py
+++ b/lms/djangoapps/course_blocks/transformers/tests/test_hidden_content.py
@@ -5,13 +5,11 @@ from datetime import timedelta
 
 import ddt
 from django.utils.timezone import now
-from nose.plugins.attrib import attr
 
 from ..hidden_content import HiddenContentTransformer
 from .helpers import BlockParentsMapTestCase, update_block
 
 
-@attr(shard=3)
 @ddt.ddt
 class HiddenContentTransformerTestCase(BlockParentsMapTestCase):
     """
@@ -19,6 +17,7 @@ class HiddenContentTransformerTestCase(BlockParentsMapTestCase):
     """
     TRANSFORMER_CLASS_TO_TEST = HiddenContentTransformer
     ALL_BLOCKS = {0, 1, 2, 3, 4, 5, 6}
+    shard = 3
 
     class DueDateType(object):
         """
diff --git a/lms/djangoapps/course_blocks/transformers/tests/test_split_test.py b/lms/djangoapps/course_blocks/transformers/tests/test_split_test.py
index 7cfe10f4424..8ac4e5b9559 100644
--- a/lms/djangoapps/course_blocks/transformers/tests/test_split_test.py
+++ b/lms/djangoapps/course_blocks/transformers/tests/test_split_test.py
@@ -2,7 +2,6 @@
 Tests for SplitTestTransformer.
 """
 import ddt
-from nose.plugins.attrib import attr
 
 import openedx.core.djangoapps.user_api.course_tag.api as course_tag_api
 from openedx.core.djangoapps.user_api.partition_schemes import RandomUserPartitionScheme
@@ -15,7 +14,6 @@ from ..user_partitions import UserPartitionTransformer, _get_user_partition_grou
 from .helpers import CourseStructureTestCase, create_location
 
 
-@attr(shard=3)
 @ddt.ddt
 class SplitTestTransformerTestCase(CourseStructureTestCase):
     """
@@ -23,6 +21,7 @@ class SplitTestTransformerTestCase(CourseStructureTestCase):
     """
     TEST_PARTITION_ID = 0
     TRANSFORMER_CLASS_TO_TEST = UserPartitionTransformer
+    shard = 3
 
     def setUp(self):
         """
diff --git a/lms/djangoapps/course_blocks/transformers/tests/test_start_date.py b/lms/djangoapps/course_blocks/transformers/tests/test_start_date.py
index a4cb0b5d3ab..a9ddea6c0b2 100644
--- a/lms/djangoapps/course_blocks/transformers/tests/test_start_date.py
+++ b/lms/djangoapps/course_blocks/transformers/tests/test_start_date.py
@@ -6,7 +6,6 @@ from datetime import timedelta
 import ddt
 from django.utils.timezone import now
 from mock import patch
-from nose.plugins.attrib import attr
 
 from courseware.tests.factories import BetaTesterFactory
 
@@ -14,7 +13,6 @@ from ..start_date import DEFAULT_START_DATE, StartDateTransformer
 from .helpers import BlockParentsMapTestCase, update_block
 
 
-@attr(shard=3)
 @ddt.ddt
 class StartDateTransformerTestCase(BlockParentsMapTestCase):
     """
@@ -23,6 +21,7 @@ class StartDateTransformerTestCase(BlockParentsMapTestCase):
     STUDENT = 1
     BETA_USER = 2
     TRANSFORMER_CLASS_TO_TEST = StartDateTransformer
+    shard = 3
 
     class StartDateType(object):
         """
diff --git a/lms/djangoapps/course_blocks/transformers/tests/test_user_partitions.py b/lms/djangoapps/course_blocks/transformers/tests/test_user_partitions.py
index 8b97a55b9e6..aa1912c34df 100644
--- a/lms/djangoapps/course_blocks/transformers/tests/test_user_partitions.py
+++ b/lms/djangoapps/course_blocks/transformers/tests/test_user_partitions.py
@@ -6,7 +6,6 @@ import string
 from collections import namedtuple
 
 import ddt
-from nose.plugins.attrib import attr
 
 from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort
 from openedx.core.djangoapps.course_groups.partition_scheme import CohortPartitionScheme
@@ -69,12 +68,13 @@ class UserPartitionTestMixin(object):
             self.partition_cohorts.append(partition_cohorts)
 
 
-@attr(shard=3)
 @ddt.ddt
 class UserPartitionTransformerTestCase(UserPartitionTestMixin, CourseStructureTestCase):
     """
     UserPartitionTransformer Test
     """
+    shard = 3
+
     def setup_partitions_and_course(self, active=True):
         """
         Setup course structure and create user for user partition
@@ -242,12 +242,13 @@ class UserPartitionTransformerTestCase(UserPartitionTestMixin, CourseStructureTe
         )
 
 
-@attr(shard=3)
 @ddt.ddt
 class MergedGroupAccessTestData(UserPartitionTestMixin, CourseStructureTestCase):
     """
     _MergedGroupAccess Test
     """
+    shard = 3
+
     def setUp(self):
         """
         Setup course structure and create user for user partition
diff --git a/lms/djangoapps/course_blocks/transformers/tests/test_visibility.py b/lms/djangoapps/course_blocks/transformers/tests/test_visibility.py
index 9bc1d01c9e4..bcdd3ebab5c 100644
--- a/lms/djangoapps/course_blocks/transformers/tests/test_visibility.py
+++ b/lms/djangoapps/course_blocks/transformers/tests/test_visibility.py
@@ -2,19 +2,18 @@
 Tests for VisibilityTransformer.
 """
 import ddt
-from nose.plugins.attrib import attr
 
 from ..visibility import VisibilityTransformer
 from .helpers import BlockParentsMapTestCase, update_block
 
 
-@attr(shard=3)
 @ddt.ddt
 class VisibilityTransformerTestCase(BlockParentsMapTestCase):
     """
     VisibilityTransformer Test
     """
     TRANSFORMER_CLASS_TO_TEST = VisibilityTransformer
+    shard = 3
 
     # Following test cases are based on BlockParentsMapTestCase.parents_map
     @ddt.data(
diff --git a/lms/djangoapps/course_wiki/tests/test_access.py b/lms/djangoapps/course_wiki/tests/test_access.py
index c5746486410..c0759794146 100644
--- a/lms/djangoapps/course_wiki/tests/test_access.py
+++ b/lms/djangoapps/course_wiki/tests/test_access.py
@@ -3,13 +3,13 @@ Tests for wiki permissions
 """
 
 from django.contrib.auth.models import Group
-from nose.plugins.attrib import attr
 from wiki.models import URLPath
 
 from course_wiki import settings
 from course_wiki.utils import course_wiki_slug, user_is_article_course_staff
 from course_wiki.views import get_or_create_root
 from courseware.tests.factories import InstructorFactory, StaffFactory
+from openedx.core.lib.tests import attr
 from student.tests.factories import UserFactory
 from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
diff --git a/lms/djangoapps/course_wiki/tests/test_comprehensive_theming.py b/lms/djangoapps/course_wiki/tests/test_comprehensive_theming.py
index 149bdfdb040..b01baebef79 100644
--- a/lms/djangoapps/course_wiki/tests/test_comprehensive_theming.py
+++ b/lms/djangoapps/course_wiki/tests/test_comprehensive_theming.py
@@ -4,7 +4,6 @@ Tests for wiki middleware.
 from unittest import skip
 
 from django.test.client import Client
-from nose.plugins.attrib import attr
 from wiki.models import URLPath
 
 from course_wiki.views import get_or_create_root
@@ -14,9 +13,9 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 class TestComprehensiveTheming(ModuleStoreTestCase):
     """Tests for comprehensive theming of wiki pages."""
+    shard = 1
 
     def setUp(self):
         """Test setup."""
diff --git a/lms/djangoapps/course_wiki/tests/test_middleware.py b/lms/djangoapps/course_wiki/tests/test_middleware.py
index 4ad9a36b006..c0d62090730 100644
--- a/lms/djangoapps/course_wiki/tests/test_middleware.py
+++ b/lms/djangoapps/course_wiki/tests/test_middleware.py
@@ -3,7 +3,6 @@ Tests for wiki middleware.
 """
 
 from django.test.client import Client
-from nose.plugins.attrib import attr
 from wiki.models import URLPath
 
 from course_wiki.views import get_or_create_root
@@ -12,9 +11,9 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 class TestWikiAccessMiddleware(ModuleStoreTestCase):
     """Tests for WikiAccessMiddleware."""
+    shard = 1
 
     def setUp(self):
         """Test setup."""
diff --git a/lms/djangoapps/course_wiki/tests/tests.py b/lms/djangoapps/course_wiki/tests/tests.py
index 6a060edb115..f8e1fba275c 100644
--- a/lms/djangoapps/course_wiki/tests/tests.py
+++ b/lms/djangoapps/course_wiki/tests/tests.py
@@ -4,7 +4,6 @@ Tests for course wiki
 
 from django.urls import reverse
 from mock import patch
-from nose.plugins.attrib import attr
 from six import text_type
 
 from courseware.tests.tests import LoginEnrollmentTestCase
@@ -13,11 +12,11 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 class WikiRedirectTestCase(EnterpriseTestConsentRequired, LoginEnrollmentTestCase, ModuleStoreTestCase):
     """
     Tests for wiki course redirection.
     """
+    shard = 1
 
     def setUp(self):
         super(WikiRedirectTestCase, self).setUp()
diff --git a/lms/djangoapps/courseware/management/commands/tests/test_dump_course.py b/lms/djangoapps/courseware/management/commands/tests/test_dump_course.py
index 1d10f6f10e2..d8b60c49283 100644
--- a/lms/djangoapps/courseware/management/commands/tests/test_dump_course.py
+++ b/lms/djangoapps/courseware/management/commands/tests/test_dump_course.py
@@ -7,7 +7,6 @@ Tests for Django management commands
 import json
 from StringIO import StringIO
 
-from nose.plugins.attrib import attr
 from six import text_type
 
 import factory
@@ -27,7 +26,6 @@ DATA_DIR = settings.COMMON_TEST_DATA_ROOT
 XML_COURSE_DIRS = ['simple']
 
 
-@attr(shard=1)
 class CommandsTestBase(SharedModuleStoreTestCase):
     """
     Base class for testing different django commands.
@@ -39,6 +37,7 @@ class CommandsTestBase(SharedModuleStoreTestCase):
     __test__ = False
     url_name = '2012_Fall'
     ENABLED_SIGNALS = ['course_published']
+    shard = 1
 
     @classmethod
     def setUpClass(cls):
diff --git a/lms/djangoapps/courseware/tests/test_about.py b/lms/djangoapps/courseware/tests/test_about.py
index 88350f45b30..cfa69b58ae7 100644
--- a/lms/djangoapps/courseware/tests/test_about.py
+++ b/lms/djangoapps/courseware/tests/test_about.py
@@ -10,12 +10,12 @@ from django.urls import reverse
 from django.test.utils import override_settings
 from milestones.tests.utils import MilestonesTestCaseMixin
 from mock import patch
-from nose.plugins.attrib import attr
 from six import text_type
 from waffle.testutils import override_switch
 
 from course_modes.models import CourseMode
 from lms.djangoapps.ccx.tests.factories import CcxFactory
+from openedx.core.lib.tests import attr
 from openedx.features.course_experience.waffle import WAFFLE_NAMESPACE as COURSE_EXPERIENCE_WAFFLE_NAMESPACE
 from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML
 from shoppingcart.models import Order, PaidCourseRegistration
diff --git a/lms/djangoapps/courseware/tests/test_access.py b/lms/djangoapps/courseware/tests/test_access.py
index 6dabede6714..260101cb740 100644
--- a/lms/djangoapps/courseware/tests/test_access.py
+++ b/lms/djangoapps/courseware/tests/test_access.py
@@ -14,7 +14,6 @@ from django.test import TestCase
 from django.test.client import RequestFactory
 from milestones.tests.utils import MilestonesTestCaseMixin
 from mock import Mock, patch
-from nose.plugins.attrib import attr
 from opaque_keys.edx.locator import CourseLocator
 
 import courseware.access as access
@@ -31,6 +30,7 @@ from courseware.tests.helpers import LoginEnrollmentTestCase, masquerade_as_grou
 from lms.djangoapps.ccx.models import CustomCourseForEdX
 from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
 from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
+from openedx.core.lib.tests import attr
 from student.models import CourseEnrollment
 from student.roles import CourseCcxCoachRole, CourseStaffRole
 from student.tests.factories import (
diff --git a/lms/djangoapps/courseware/tests/test_course_info.py b/lms/djangoapps/courseware/tests/test_course_info.py
index 156a4c81191..1d15f3b60fc 100644
--- a/lms/djangoapps/courseware/tests/test_course_info.py
+++ b/lms/djangoapps/courseware/tests/test_course_info.py
@@ -11,10 +11,10 @@ from django.test.utils import override_settings
 
 from ccx_keys.locator import CCXLocator
 from lms.djangoapps.ccx.tests.factories import CcxFactory
-from nose.plugins.attrib import attr
 from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
 from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context
 from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES, override_waffle_flag
+from openedx.core.lib.tests import attr
 from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG
 from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
 from pyquery import PyQuery as pq
diff --git a/lms/djangoapps/courseware/tests/test_course_survey.py b/lms/djangoapps/courseware/tests/test_course_survey.py
index c51e8abb2ad..995639033d2 100644
--- a/lms/djangoapps/courseware/tests/test_course_survey.py
+++ b/lms/djangoapps/courseware/tests/test_course_survey.py
@@ -7,7 +7,6 @@ from copy import deepcopy
 
 from django.contrib.auth.models import User
 from django.urls import reverse
-from nose.plugins.attrib import attr
 
 from common.test.utils import XssTestMixin
 from courseware.tests.helpers import LoginEnrollmentTestCase
@@ -16,12 +15,11 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 class SurveyViewsTests(LoginEnrollmentTestCase, SharedModuleStoreTestCase, XssTestMixin):
     """
     All tests for the views.py file
     """
-
+    shard = 1
     STUDENT_INFO = [('view@test.com', 'foo')]
 
     @classmethod
diff --git a/lms/djangoapps/courseware/tests/test_course_tools.py b/lms/djangoapps/courseware/tests/test_course_tools.py
index 8060e1e4e4e..57214813c0f 100644
--- a/lms/djangoapps/courseware/tests/test_course_tools.py
+++ b/lms/djangoapps/courseware/tests/test_course_tools.py
@@ -6,7 +6,6 @@ import crum
 import datetime
 
 from mock import patch
-from nose.plugins.attrib import attr
 import pytz
 from django.test import RequestFactory
 
@@ -23,8 +22,8 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=3)
 class VerifiedUpgradeToolTest(SharedModuleStoreTestCase):
+    shard = 3
 
     @classmethod
     def setUpClass(cls):
diff --git a/lms/djangoapps/courseware/tests/test_courses.py b/lms/djangoapps/courseware/tests/test_courses.py
index 92454ee2021..8f15eb73d00 100644
--- a/lms/djangoapps/courseware/tests/test_courses.py
+++ b/lms/djangoapps/courseware/tests/test_courses.py
@@ -12,7 +12,6 @@ from django.conf import settings
 from django.urls import reverse
 from django.test.client import RequestFactory
 from django.test.utils import override_settings
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 from six import text_type
 
@@ -34,6 +33,7 @@ from courseware.module_render import get_module_for_descriptor
 from lms.djangoapps.courseware.courseware_access_exception import CoursewareAccessException
 from openedx.core.djangolib.testing.utils import get_mock_request
 from openedx.core.lib.courses import course_image_url
+from openedx.core.lib.tests import attr
 from student.tests.factories import UserFactory
 from xmodule.modulestore import ModuleStoreEnum
 from xmodule.modulestore.django import _get_modulestore_branch_setting, modulestore
diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py
index b58195f8645..30c13deb3f6 100644
--- a/lms/djangoapps/courseware/tests/test_date_summary.py
+++ b/lms/djangoapps/courseware/tests/test_date_summary.py
@@ -9,7 +9,6 @@ from django.urls import reverse
 from django.test import RequestFactory
 from freezegun import freeze_time
 from mock import patch
-from nose.plugins.attrib import attr
 from pytz import utc
 
 from course_modes.models import CourseMode
@@ -37,6 +36,7 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
 from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
 from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
 from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
+from openedx.core.lib.tests import attr
 from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG, UPGRADE_DEADLINE_MESSAGE, CourseHomeMessages
 from student.tests.factories import TEST_PASSWORD, CourseEnrollmentFactory, UserFactory
 from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
diff --git a/lms/djangoapps/courseware/tests/test_draft_modulestore.py b/lms/djangoapps/courseware/tests/test_draft_modulestore.py
index dac9675b73d..9566f908292 100644
--- a/lms/djangoapps/courseware/tests/test_draft_modulestore.py
+++ b/lms/djangoapps/courseware/tests/test_draft_modulestore.py
@@ -1,15 +1,15 @@
 from django.test import TestCase
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 
 from xmodule.modulestore.django import modulestore
 
 
-@attr(shard=1)
 class TestDraftModuleStore(TestCase):
     """
     Test the draft modulestore
     """
+    shard = 1
+
     def test_get_items_with_course_items(self):
         store = modulestore()
 
diff --git a/lms/djangoapps/courseware/tests/test_entrance_exam.py b/lms/djangoapps/courseware/tests/test_entrance_exam.py
index eeeab157a40..196623f5fb1 100644
--- a/lms/djangoapps/courseware/tests/test_entrance_exam.py
+++ b/lms/djangoapps/courseware/tests/test_entrance_exam.py
@@ -17,7 +17,6 @@ from courseware.module_render import get_module, handle_xblock_callback, toc_for
 from courseware.tests.factories import InstructorFactory, StaffFactory, UserFactory
 from courseware.tests.helpers import LoginEnrollmentTestCase
 from milestones.tests.utils import MilestonesTestCaseMixin
-from nose.plugins.attrib import attr
 from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
 from openedx.core.djangolib.testing.utils import get_mock_request
 from openedx.features.course_experience import COURSE_OUTLINE_PAGE_FLAG, UNIFIED_COURSE_TAB_FLAG
@@ -36,7 +35,6 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
 
 
-@attr(shard=2)
 @patch.dict('django.conf.settings.FEATURES', {'ENTRANCE_EXAMS': True})
 class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTestCaseMixin):
     """
@@ -45,6 +43,8 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase, Milest
     Creates a test course from scratch. The tests below are designed to execute
     workflows regardless of the feature flag settings.
     """
+    shard = 2
+
     @patch.dict('django.conf.settings.FEATURES', {'ENTRANCE_EXAMS': True})
     def setUp(self):
         """
diff --git a/lms/djangoapps/courseware/tests/test_favicon.py b/lms/djangoapps/courseware/tests/test_favicon.py
index 5152bce4efa..9450f2fe706 100644
--- a/lms/djangoapps/courseware/tests/test_favicon.py
+++ b/lms/djangoapps/courseware/tests/test_favicon.py
@@ -1,15 +1,14 @@
 from django.test import TestCase
 from django.test.utils import override_settings
-from nose.plugins.attrib import attr
 
 from util.testing import UrlResetMixin
 
 
-@attr(shard=1)
 class FaviconTestCase(UrlResetMixin, TestCase):
     """
     Tests of the courseware favicon.
     """
+    shard = 1
 
     def test_favicon_redirect(self):
         resp = self.client.get("/favicon.ico")
diff --git a/lms/djangoapps/courseware/tests/test_field_overrides.py b/lms/djangoapps/courseware/tests/test_field_overrides.py
index 7cb98b94162..0478dd830ca 100644
--- a/lms/djangoapps/courseware/tests/test_field_overrides.py
+++ b/lms/djangoapps/courseware/tests/test_field_overrides.py
@@ -5,9 +5,9 @@ Tests for `field_overrides` module.
 import unittest
 
 from django.test.utils import override_settings
-from nose.plugins.attrib import attr
 from xblock.field_data import DictFieldData
 
+from openedx.core.lib.tests import attr
 from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
diff --git a/lms/djangoapps/courseware/tests/test_footer.py b/lms/djangoapps/courseware/tests/test_footer.py
index c655afe8e21..2b76db6d992 100644
--- a/lms/djangoapps/courseware/tests/test_footer.py
+++ b/lms/djangoapps/courseware/tests/test_footer.py
@@ -8,17 +8,16 @@ import unittest
 from django.conf import settings
 from django.test import TestCase
 from django.test.utils import override_settings
-from nose.plugins.attrib import attr
 
 from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
 
 
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
-@attr(shard=1)
 class TestFooter(TestCase):
     """
     Tests for edx and OpenEdX footer
     """
+    shard = 1
 
     SOCIAL_MEDIA_NAMES = [
         "facebook",
diff --git a/lms/djangoapps/courseware/tests/test_group_access.py b/lms/djangoapps/courseware/tests/test_group_access.py
index bec7062f6ad..581cbb60871 100644
--- a/lms/djangoapps/courseware/tests/test_group_access.py
+++ b/lms/djangoapps/courseware/tests/test_group_access.py
@@ -4,7 +4,6 @@ access control rules.
 """
 
 import ddt
-from nose.plugins.attrib import attr
 from stevedore.extension import Extension, ExtensionManager
 
 import courseware.access as access
@@ -50,13 +49,14 @@ def resolve_attrs(test_method):
     return _wrapper
 
 
-@attr(shard=7)
 @ddt.ddt
 class GroupAccessTestCase(ModuleStoreTestCase):
     """
     Tests to ensure that has_access() correctly enforces the visibility
     restrictions specified in the `group_access` field of XBlocks.
     """
+    shard = 7
+
     def set_user_group(self, user, partition, group):
         """
         Internal DRY / shorthand.
diff --git a/lms/djangoapps/courseware/tests/test_i18n.py b/lms/djangoapps/courseware/tests/test_i18n.py
index 939cc697935..71fd9c48402 100644
--- a/lms/djangoapps/courseware/tests/test_i18n.py
+++ b/lms/djangoapps/courseware/tests/test_i18n.py
@@ -11,10 +11,10 @@ from django.urls import reverse, reverse_lazy
 from django.test import TestCase
 from django.test.client import Client
 from django.utils import translation
-from nose.plugins.attrib import attr
 
 from openedx.core.djangoapps.dark_lang.models import DarkLangConfig
 from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
+from openedx.core.lib.tests import attr
 from student.tests.factories import UserFactory
 
 
diff --git a/lms/djangoapps/courseware/tests/test_lti_integration.py b/lms/djangoapps/courseware/tests/test_lti_integration.py
index 007489ec1b0..a6a7e4ba375 100644
--- a/lms/djangoapps/courseware/tests/test_lti_integration.py
+++ b/lms/djangoapps/courseware/tests/test_lti_integration.py
@@ -5,14 +5,12 @@ import urllib
 from collections import OrderedDict
 
 import mock
-import pytest
 from django.conf import settings
 from django.urls import reverse
 
 import oauthlib
 from courseware.tests.helpers import BaseTestXmodule
 from courseware.views.views import get_course_lti_endpoints
-from nose.plugins.attrib import attr
 from openedx.core.lib.url_utils import quote_slashes
 from six import text_type
 from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
@@ -20,7 +18,6 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
 from xmodule.x_module import STUDENT_VIEW
 
 
-@attr(shard=1)
 class TestLTI(BaseTestXmodule):
     """
     Integration test for lti xmodule.
@@ -30,6 +27,7 @@ class TestLTI(BaseTestXmodule):
     of `oauthlib` library.
     """
     CATEGORY = "lti"
+    shard = 1
 
     def setUp(self):
         """
@@ -126,7 +124,6 @@ class TestLTI(BaseTestXmodule):
         self.assertEqual(generated_content, expected_content)
 
 
-@attr(shard=1)
 class TestLTIModuleListing(SharedModuleStoreTestCase):
     """
     a test for the rest endpoint that lists LTI modules in a course
@@ -134,6 +131,7 @@ class TestLTIModuleListing(SharedModuleStoreTestCase):
     # arbitrary constant
     COURSE_SLUG = "100"
     COURSE_NAME = "test_course"
+    shard = 1
 
     @classmethod
     def setUpClass(cls):
diff --git a/lms/djangoapps/courseware/tests/test_masquerade.py b/lms/djangoapps/courseware/tests/test_masquerade.py
index 67c86a26486..bbff1573279 100644
--- a/lms/djangoapps/courseware/tests/test_masquerade.py
+++ b/lms/djangoapps/courseware/tests/test_masquerade.py
@@ -18,11 +18,11 @@ from courseware.masquerade import CourseMasquerade, MasqueradingKeyValueStore, g
 from courseware.tests.factories import StaffFactory
 from courseware.tests.helpers import LoginEnrollmentTestCase, masquerade_as_group_member
 from courseware.tests.test_submitting_problems import ProblemSubmissionTestMixin
-from nose.plugins.attrib import attr
 from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
 from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
 from openedx.core.djangoapps.user_api.preferences.api import get_user_preference, set_user_preference
 from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
+from openedx.core.lib.tests import attr
 from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG
 from student.models import CourseEnrollment
 from student.tests.factories import UserFactory
diff --git a/lms/djangoapps/courseware/tests/test_microsites.py b/lms/djangoapps/courseware/tests/test_microsites.py
index 7fb321144df..e474b62da2b 100644
--- a/lms/djangoapps/courseware/tests/test_microsites.py
+++ b/lms/djangoapps/courseware/tests/test_microsites.py
@@ -2,14 +2,12 @@
 Tests related to the Site Configuration feature
 """
 
-import pytest
 from bs4 import BeautifulSoup
 from contextlib import contextmanager
 from django.conf import settings
 from django.urls import reverse
 from django.test.utils import override_settings
 from mock import patch
-from nose.plugins.attrib import attr
 from six import text_type
 
 from course_modes.models import CourseMode
@@ -19,12 +17,11 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
 
 
-@attr(shard=1)
 class TestSites(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
     """
     This is testing of the Site Configuration feature
     """
-
+    shard = 1
     STUDENT_INFO = [('view@test.com', 'foo'), ('view2@test.com', 'foo')]
     ENABLED_SIGNALS = ['course_published']
 
diff --git a/lms/djangoapps/courseware/tests/test_middleware.py b/lms/djangoapps/courseware/tests/test_middleware.py
index addfeb9f3b9..a83996bae90 100644
--- a/lms/djangoapps/courseware/tests/test_middleware.py
+++ b/lms/djangoapps/courseware/tests/test_middleware.py
@@ -4,7 +4,6 @@ Tests for courseware middleware
 
 from django.http import Http404
 from django.test.client import RequestFactory
-from nose.plugins.attrib import attr
 
 from lms.djangoapps.courseware.exceptions import Redirect
 from lms.djangoapps.courseware.middleware import RedirectMiddleware
@@ -12,9 +11,9 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 class CoursewareMiddlewareTestCase(SharedModuleStoreTestCase):
     """Tests that courseware middleware is correctly redirected"""
+    shard = 1
 
     @classmethod
     def setUpClass(cls):
diff --git a/lms/djangoapps/courseware/tests/test_model_data.py b/lms/djangoapps/courseware/tests/test_model_data.py
index 14320d95aa0..d5a769c9842 100644
--- a/lms/djangoapps/courseware/tests/test_model_data.py
+++ b/lms/djangoapps/courseware/tests/test_model_data.py
@@ -7,7 +7,6 @@ from functools import partial
 from django.db import DatabaseError
 from django.test import TestCase
 from mock import Mock, patch
-from nose.plugins.attrib import attr
 from xblock.core import XBlock
 from xblock.exceptions import KeyValueMultiSaveError
 from xblock.fields import BlockScope, Scope, ScopeIds
@@ -27,6 +26,7 @@ from courseware.tests.factories import (
     course_id,
     location
 )
+from openedx.core.lib.tests import attr
 from student.tests.factories import UserFactory
 
 
diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py
index ae2eab1c96d..04b68f2fa4e 100644
--- a/lms/djangoapps/courseware/tests/test_module_render.py
+++ b/lms/djangoapps/courseware/tests/test_module_render.py
@@ -8,7 +8,6 @@ from datetime import datetime
 from functools import partial
 
 import ddt
-import pytest
 import pytz
 from bson import ObjectId
 from completion.models import BlockCompletion
@@ -25,7 +24,6 @@ from edx_proctoring.tests.test_services import MockCreditService, MockGradesServ
 from freezegun import freeze_time
 from milestones.tests.utils import MilestonesTestCaseMixin
 from mock import MagicMock, Mock, patch
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey, UsageKey
 from pyquery import PyQuery
 from six import text_type
@@ -53,6 +51,7 @@ from openedx.core.djangoapps.credit.api import set_credit_requirement_status, se
 from openedx.core.djangoapps.credit.models import CreditCourse
 from openedx.core.lib.courses import course_image_url
 from openedx.core.lib.gating import api as gating_api
+from openedx.core.lib.tests import attr
 from openedx.core.lib.url_utils import quote_slashes
 from student.models import anonymous_id_for_user
 from verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory
diff --git a/lms/djangoapps/courseware/tests/test_navigation.py b/lms/djangoapps/courseware/tests/test_navigation.py
index e5f61a34c5f..b6397abeb1e 100644
--- a/lms/djangoapps/courseware/tests/test_navigation.py
+++ b/lms/djangoapps/courseware/tests/test_navigation.py
@@ -7,7 +7,6 @@ from django.conf import settings
 from django.urls import reverse
 from django.test.utils import override_settings
 from mock import patch
-from nose.plugins.attrib import attr
 from six import text_type
 
 from courseware.tests.factories import GlobalStaffFactory
@@ -20,12 +19,11 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
 
 
-@attr(shard=1)
 class TestNavigation(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
     """
     Check that navigation state is saved properly.
     """
-
+    shard = 1
     STUDENT_INFO = [('view@test.com', 'foo'), ('view2@test.com', 'foo')]
 
     @classmethod
diff --git a/lms/djangoapps/courseware/tests/test_password_history.py b/lms/djangoapps/courseware/tests/test_password_history.py
index 05d6ad72ffd..89b7f6640d7 100644
--- a/lms/djangoapps/courseware/tests/test_password_history.py
+++ b/lms/djangoapps/courseware/tests/test_password_history.py
@@ -14,19 +14,18 @@ from django.utils import timezone
 from django.utils.http import int_to_base36
 from freezegun import freeze_time
 from mock import patch
-from nose.plugins.attrib import attr
 
 from courseware.tests.helpers import LoginEnrollmentTestCase
 from student.models import PasswordHistory
 
 
-@attr(shard=1)
 @patch.dict("django.conf.settings.FEATURES", {'ADVANCED_SECURITY': True})
 @ddt.ddt
 class TestPasswordHistory(LoginEnrollmentTestCase):
     """
     Go through some of the PasswordHistory use cases
     """
+    shard = 1
 
     def _login(self, email, password, should_succeed=True, err_msg_check=None):
         """
diff --git a/lms/djangoapps/courseware/tests/test_split_module.py b/lms/djangoapps/courseware/tests/test_split_module.py
index 41b1fd91421..4d72046af70 100644
--- a/lms/djangoapps/courseware/tests/test_split_module.py
+++ b/lms/djangoapps/courseware/tests/test_split_module.py
@@ -3,7 +3,6 @@ Test for split test XModule
 """
 from django.urls import reverse
 from mock import MagicMock
-from nose.plugins.attrib import attr
 from six import text_type
 
 from courseware.model_data import FieldDataCache
@@ -15,7 +14,6 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
 from xmodule.partitions.partitions import Group, UserPartition
 
 
-@attr(shard=1)
 class SplitTestBase(SharedModuleStoreTestCase):
     """
     Sets up a basic course and user for split test testing.
@@ -26,6 +24,7 @@ class SplitTestBase(SharedModuleStoreTestCase):
     ICON_CLASSES = None
     TOOLTIPS = None
     VISIBLE_CONTENT = None
+    shard = 1
 
     @classmethod
     def setUpClass(cls):
@@ -285,11 +284,12 @@ class TestVertSplitTestVert(SplitTestBase):
         ]
 
 
-@attr(shard=1)
 class SplitTestPosition(SharedModuleStoreTestCase):
     """
     Check that we can change positions in a course with partitions defined
     """
+    shard = 1
+
     @classmethod
     def setUpClass(cls):
         super(SplitTestPosition, cls).setUpClass()
diff --git a/lms/djangoapps/courseware/tests/test_submitting_problems.py b/lms/djangoapps/courseware/tests/test_submitting_problems.py
index 5505520ad16..0b0eb1b544c 100644
--- a/lms/djangoapps/courseware/tests/test_submitting_problems.py
+++ b/lms/djangoapps/courseware/tests/test_submitting_problems.py
@@ -17,7 +17,6 @@ from django.test import TestCase
 from django.test.client import RequestFactory
 from django.utils.timezone import now
 from mock import patch
-from nose.plugins.attrib import attr
 from six import text_type
 
 from capa.tests.response_xml_factory import (
@@ -34,6 +33,7 @@ from lms.djangoapps.grades.tasks import compute_all_grades_for_course
 from openedx.core.djangoapps.credit.api import get_credit_requirement_status, set_credit_requirements
 from openedx.core.djangoapps.credit.models import CreditCourse, CreditProvider
 from openedx.core.djangoapps.user_api.tests.factories import UserCourseTagFactory
+from openedx.core.lib.tests import attr
 from openedx.core.lib.url_utils import quote_slashes
 from student.models import CourseEnrollment, anonymous_id_for_user
 from submissions import api as submissions_api
diff --git a/lms/djangoapps/courseware/tests/test_tabs.py b/lms/djangoapps/courseware/tests/test_tabs.py
index e261c252c82..95f47751c19 100644
--- a/lms/djangoapps/courseware/tests/test_tabs.py
+++ b/lms/djangoapps/courseware/tests/test_tabs.py
@@ -2,13 +2,11 @@
 Test cases for tabs.
 """
 
-import pytest
 from django.contrib.auth.models import AnonymousUser
 from django.urls import reverse
 from django.http import Http404
 from milestones.tests.utils import MilestonesTestCaseMixin
 from mock import MagicMock, Mock, patch
-from nose.plugins.attrib import attr
 from six import text_type
 
 from courseware.courses import get_course_by_id
@@ -25,6 +23,7 @@ from courseware.tests.helpers import LoginEnrollmentTestCase
 from courseware.views.views import StaticCourseTabView, get_static_tab_fragment
 from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
 from openedx.core.djangolib.testing.utils import get_mock_request
+from openedx.core.lib.tests import attr
 from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG
 from student.models import CourseEnrollment
 from student.tests.factories import UserFactory
diff --git a/lms/djangoapps/courseware/tests/test_video_handlers.py b/lms/djangoapps/courseware/tests/test_video_handlers.py
index d0b1e7a4617..d4a783f730a 100644
--- a/lms/djangoapps/courseware/tests/test_video_handlers.py
+++ b/lms/djangoapps/courseware/tests/test_video_handlers.py
@@ -12,11 +12,11 @@ import freezegun
 from django.core.files.base import ContentFile
 from django.utils.timezone import now
 from mock import MagicMock, Mock, patch
-from nose.plugins.attrib import attr
 from webob import Request, Response
 
 from common.test.utils import normalize_repr
 from openedx.core.djangoapps.contentserver.caching import del_cached_content
+from openedx.core.lib.tests import attr
 from xmodule.contentstore.content import StaticContent
 from xmodule.contentstore.django import contentstore
 from xmodule.exceptions import NotFoundError
diff --git a/lms/djangoapps/courseware/tests/test_video_mongo.py b/lms/djangoapps/courseware/tests/test_video_mongo.py
index 37eb51e7586..0fd5f909681 100644
--- a/lms/djangoapps/courseware/tests/test_video_mongo.py
+++ b/lms/djangoapps/courseware/tests/test_video_mongo.py
@@ -31,9 +31,9 @@ from edxval.api import (
 from edxval.utils import create_file_in_fs
 from lxml import etree
 from mock import MagicMock, Mock, patch
-from nose.plugins.attrib import attr
 from path import Path as path
 
+from openedx.core.lib.tests import attr
 from xmodule.contentstore.content import StaticContent
 from xmodule.exceptions import NotFoundError
 from xmodule.modulestore import ModuleStoreEnum
diff --git a/lms/djangoapps/courseware/tests/test_video_xml.py b/lms/djangoapps/courseware/tests/test_video_xml.py
index 6c789f86727..b3fb6d9f4dd 100644
--- a/lms/djangoapps/courseware/tests/test_video_xml.py
+++ b/lms/djangoapps/courseware/tests/test_video_xml.py
@@ -14,8 +14,6 @@ You can then use the CourseFactory and XModuleItemFactory as defined in
 common/lib/xmodule/xmodule/modulestore/tests/factories.py to create the
 course, section, subsection, unit, etc.
 """
-from nose.plugins.attrib import attr
-
 from xmodule.tests import LogicTest
 from xmodule.video_module import VideoDescriptor
 
@@ -35,7 +33,6 @@ SOURCE_XML = """
 """
 
 
-@attr(shard=1)
 class VideoModuleLogicTest(LogicTest):
     """Tests for logic of Video Xmodule."""
 
@@ -44,6 +41,7 @@ class VideoModuleLogicTest(LogicTest):
     raw_field_data = {
         'data': '<video />'
     }
+    shard = 1
 
     def test_parse_youtube(self):
         """Test parsing old-style Youtube ID strings into a dict."""
diff --git a/lms/djangoapps/courseware/tests/test_view_authentication.py b/lms/djangoapps/courseware/tests/test_view_authentication.py
index c0bd58c7350..c96db5a498d 100644
--- a/lms/djangoapps/courseware/tests/test_view_authentication.py
+++ b/lms/djangoapps/courseware/tests/test_view_authentication.py
@@ -3,7 +3,6 @@ import datetime
 import pytz
 from django.urls import reverse
 from mock import patch
-from nose.plugins.attrib import attr
 from six import text_type
 
 from courseware.access import has_access
@@ -23,7 +22,6 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
 
 
-@attr(shard=1)
 class TestViewAuth(EnterpriseTestConsentRequired, ModuleStoreTestCase, LoginEnrollmentTestCase):
     """
     Check that view authentication works properly.
@@ -31,6 +29,7 @@ class TestViewAuth(EnterpriseTestConsentRequired, ModuleStoreTestCase, LoginEnro
 
     ACCOUNT_INFO = [('view@test.com', 'foo'), ('view2@test.com', 'foo')]
     ENABLED_SIGNALS = ['course_published']
+    shard = 1
 
     @staticmethod
     def _reverse_urls(names, course):
@@ -410,11 +409,12 @@ class TestViewAuth(EnterpriseTestConsentRequired, ModuleStoreTestCase, LoginEnro
         self.assertTrue(self.enroll(self.course))
 
 
-@attr(shard=1)
 class TestBetatesterAccess(ModuleStoreTestCase, CourseAccessTestMixin):
     """
     Tests for the beta tester feature
     """
+    shard = 1
+
     def setUp(self):
         super(TestBetatesterAccess, self).setUp()
 
diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py
index e03ba80d967..e0d43ad3ae6 100644
--- a/lms/djangoapps/courseware/tests/test_views.py
+++ b/lms/djangoapps/courseware/tests/test_views.py
@@ -22,7 +22,6 @@ from django.test.utils import override_settings
 from freezegun import freeze_time
 from milestones.tests.utils import MilestonesTestCaseMixin
 from mock import MagicMock, PropertyMock, create_autospec, patch
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
 from pytz import UTC
@@ -62,6 +61,7 @@ from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag, WaffleFlagNam
 from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES, override_waffle_flag
 from openedx.core.djangolib.testing.utils import get_mock_request
 from openedx.core.lib.gating import api as gating_api
+from openedx.core.lib.tests import attr
 from openedx.core.lib.url_utils import quote_slashes
 from openedx.features.course_experience import COURSE_OUTLINE_PAGE_FLAG, UNIFIED_COURSE_TAB_FLAG
 from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired
diff --git a/lms/djangoapps/courseware/tests/test_word_cloud.py b/lms/djangoapps/courseware/tests/test_word_cloud.py
index 7e3ee0d74f6..5b39bfd264c 100644
--- a/lms/djangoapps/courseware/tests/test_word_cloud.py
+++ b/lms/djangoapps/courseware/tests/test_word_cloud.py
@@ -4,17 +4,15 @@
 import json
 from operator import itemgetter
 
-from nose.plugins.attrib import attr
-
 from xmodule.x_module import STUDENT_VIEW
 
 from .helpers import BaseTestXmodule
 
 
-@attr(shard=1)
 class TestWordCloud(BaseTestXmodule):
     """Integration test for word cloud xmodule."""
     CATEGORY = "word_cloud"
+    shard = 1
 
     def _get_resource_url(self, item):
         """
diff --git a/lms/djangoapps/courseware/tests/tests.py b/lms/djangoapps/courseware/tests/tests.py
index 384d7a92e13..ccebc4a977e 100644
--- a/lms/djangoapps/courseware/tests/tests.py
+++ b/lms/djangoapps/courseware/tests/tests.py
@@ -6,12 +6,12 @@ from unittest import TestCase
 
 import mock
 from django.urls import reverse
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 from six import text_type
 
 from courseware.tests.helpers import LoginEnrollmentTestCase
 from lms.djangoapps.lms_xblock.field_data import LmsFieldData
+from openedx.core.lib.tests import attr
 from xmodule.error_module import ErrorDescriptor
 from xmodule.modulestore.django import modulestore
 from xmodule.modulestore.tests.django_utils import TEST_DATA_MIXED_MODULESTORE, ModuleStoreTestCase
diff --git a/lms/djangoapps/dashboard/management/commands/tests/test_git_add_course.py b/lms/djangoapps/dashboard/management/commands/tests/test_git_add_course.py
index 9bb05a46939..f6055301eb1 100644
--- a/lms/djangoapps/dashboard/management/commands/tests/test_git_add_course.py
+++ b/lms/djangoapps/dashboard/management/commands/tests/test_git_add_course.py
@@ -13,7 +13,6 @@ from django.conf import settings
 from django.core.management import call_command
 from django.core.management.base import CommandError
 from django.test.utils import override_settings
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 
 import dashboard.git_import as git_import
@@ -42,7 +41,6 @@ FEATURES_WITH_SSL_AUTH = settings.FEATURES.copy()
 FEATURES_WITH_SSL_AUTH['AUTH_USE_CERTIFICATES'] = True
 
 
-@attr(shard=3)
 @override_settings(
     MONGODB_LOG=TEST_MONGODB_LOG,
     GIT_REPO_DIR=settings.TEST_ROOT / "course_repos_{}".format(uuid4().hex)
@@ -53,7 +51,7 @@ class TestGitAddCourse(SharedModuleStoreTestCase):
     """
     Tests the git_add_course management command for proper functions.
     """
-
+    shard = 3
     TEST_REPO = 'https://github.com/mitocw/edx4edx_lite.git'
     TEST_COURSE = 'MITx/edx4edx/edx4edx'
     TEST_BRANCH = 'testing_do_not_delete'
diff --git a/lms/djangoapps/dashboard/tests/test_sysadmin.py b/lms/djangoapps/dashboard/tests/test_sysadmin.py
index fe3304a0ad8..3309263e533 100644
--- a/lms/djangoapps/dashboard/tests/test_sysadmin.py
+++ b/lms/djangoapps/dashboard/tests/test_sysadmin.py
@@ -15,7 +15,6 @@ from django.urls import reverse
 from django.test.client import Client
 from django.test.utils import override_settings
 from pytz import UTC
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 from six import text_type
 
@@ -109,7 +108,6 @@ class SysadminBaseTestCase(SharedModuleStoreTestCase):
         self.addCleanup(shutil.rmtree, path)
 
 
-@attr(shard=1)
 @override_settings(
     MONGODB_LOG=TEST_MONGODB_LOG,
     GIT_REPO_DIR=settings.TEST_ROOT / "course_repos_{}".format(uuid4().hex)
@@ -120,6 +118,7 @@ class TestSysAdminMongoCourseImport(SysadminBaseTestCase):
     """
     Check that importing into the mongo module store works
     """
+    shard = 1
 
     @classmethod
     def tearDownClass(cls):
diff --git a/lms/djangoapps/instructor/tests/test_access.py b/lms/djangoapps/instructor/tests/test_access.py
index c203392b480..1024b6e85e6 100644
--- a/lms/djangoapps/instructor/tests/test_access.py
+++ b/lms/djangoapps/instructor/tests/test_access.py
@@ -2,11 +2,11 @@
 Test instructor.access
 """
 
-from nose.plugins.attrib import attr
 from nose.tools import raises
 
 from django_comment_common.models import FORUM_ROLE_MODERATOR, Role
 from lms.djangoapps.instructor.access import allow_access, list_with_level, revoke_access, update_forum_role
+from openedx.core.lib.tests import attr
 from student.roles import CourseBetaTesterRole, CourseCcxCoachRole, CourseStaffRole
 from student.tests.factories import UserFactory
 from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py
index 9cb04bf14d0..c5594eb88e0 100644
--- a/lms/djangoapps/instructor/tests/test_api.py
+++ b/lms/djangoapps/instructor/tests/test_api.py
@@ -23,7 +23,6 @@ from django.test.utils import override_settings
 from pytz import UTC
 from django.utils.translation import ugettext as _
 from mock import Mock, NonCallableMock, patch
-from nose.plugins.attrib import attr
 from nose.tools import raises
 from opaque_keys.edx.keys import CourseKey
 from opaque_keys.edx.locator import UsageKey
@@ -62,6 +61,7 @@ from lms.djangoapps.instructor_task.api_helper import (
 from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted
 from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
 from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
+from openedx.core.lib.tests import attr
 from openedx.core.lib.xblock_utils import grade_histogram
 from shoppingcart.models import (
     Coupon,
diff --git a/lms/djangoapps/instructor/tests/test_api_email_localization.py b/lms/djangoapps/instructor/tests/test_api_email_localization.py
index a6c3e0a6412..e4af756a8d4 100644
--- a/lms/djangoapps/instructor/tests/test_api_email_localization.py
+++ b/lms/djangoapps/instructor/tests/test_api_email_localization.py
@@ -6,7 +6,6 @@ Unit tests for the localization of emails sent by instructor.api methods.
 from django.core import mail
 from django.urls import reverse
 from django.test.utils import override_settings
-from nose.plugins.attrib import attr
 from six import text_type
 
 from courseware.tests.factories import InstructorFactory
@@ -18,12 +17,13 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 class TestInstructorAPIEnrollmentEmailLocalization(SharedModuleStoreTestCase):
     """
     Test whether the enroll, unenroll and beta role emails are sent in the
     proper language, i.e: the student's language.
     """
+    shard = 1
+
     @classmethod
     def setUpClass(cls):
         super(TestInstructorAPIEnrollmentEmailLocalization, cls).setUpClass()
diff --git a/lms/djangoapps/instructor/tests/test_certificates.py b/lms/djangoapps/instructor/tests/test_certificates.py
index 54345f14ab1..72f02245f0b 100644
--- a/lms/djangoapps/instructor/tests/test_certificates.py
+++ b/lms/djangoapps/instructor/tests/test_certificates.py
@@ -13,7 +13,6 @@ from django.core.exceptions import ObjectDoesNotExist
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.urls import reverse
 from django.test.utils import override_settings
-from nose.plugins.attrib import attr
 
 from capa.xqueue_interface import XQueueInterface
 from lms.djangoapps.certificates import api as certs_api
@@ -34,6 +33,7 @@ from courseware.tests.factories import GlobalStaffFactory, InstructorFactory, Us
 from lms.djangoapps.grades.tests.utils import mock_passing_grade
 from lms.djangoapps.verify_student.services import IDVerificationService
 from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory
+from openedx.core.lib.tests import attr
 from student.models import CourseEnrollment
 from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
diff --git a/lms/djangoapps/instructor/tests/test_ecommerce.py b/lms/djangoapps/instructor/tests/test_ecommerce.py
index a9bbbe3a86b..25ad7e6d224 100644
--- a/lms/djangoapps/instructor/tests/test_ecommerce.py
+++ b/lms/djangoapps/instructor/tests/test_ecommerce.py
@@ -6,7 +6,6 @@ import datetime
 
 import pytz
 from django.urls import reverse
-from nose.plugins.attrib import attr
 from six import text_type
 
 from course_modes.models import CourseMode
@@ -18,11 +17,12 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 class TestECommerceDashboardViews(SiteMixin, SharedModuleStoreTestCase):
     """
     Check for E-commerce view on the new instructor dashboard
     """
+    shard = 1
+
     @classmethod
     def setUpClass(cls):
         super(TestECommerceDashboardViews, cls).setUpClass()
diff --git a/lms/djangoapps/instructor/tests/test_email.py b/lms/djangoapps/instructor/tests/test_email.py
index f2f3be05f5e..44d68ef4fb0 100644
--- a/lms/djangoapps/instructor/tests/test_email.py
+++ b/lms/djangoapps/instructor/tests/test_email.py
@@ -6,7 +6,6 @@ that the view is conditionally available when Course Auth is turned on.
 """
 
 from django.urls import reverse
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 from six import text_type
 
@@ -16,12 +15,13 @@ from xmodule.modulestore.tests.django_utils import TEST_DATA_MIXED_MODULESTORE,
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 class TestNewInstructorDashboardEmailViewMongoBacked(SharedModuleStoreTestCase):
     """
     Check for email view on the new instructor dashboard
     for Mongo-backed courses
     """
+    shard = 1
+
     @classmethod
     def setUpClass(cls):
         super(TestNewInstructorDashboardEmailViewMongoBacked, cls).setUpClass()
@@ -109,13 +109,13 @@ class TestNewInstructorDashboardEmailViewMongoBacked(SharedModuleStoreTestCase):
         self.assertNotIn(self.email_link, response.content)
 
 
-@attr(shard=1)
 class TestNewInstructorDashboardEmailViewXMLBacked(SharedModuleStoreTestCase):
     """
     Check for email view on the new instructor dashboard
     """
 
     MODULESTORE = TEST_DATA_MIXED_MODULESTORE
+    shard = 1
 
     @classmethod
     def setUpClass(cls):
diff --git a/lms/djangoapps/instructor/tests/test_enrollment.py b/lms/djangoapps/instructor/tests/test_enrollment.py
index 0271e34bff8..542da1e55b9 100644
--- a/lms/djangoapps/instructor/tests/test_enrollment.py
+++ b/lms/djangoapps/instructor/tests/test_enrollment.py
@@ -12,7 +12,6 @@ from django.conf import settings
 from django.utils.translation import override as override_language
 from django.utils.translation import get_language
 from mock import patch
-from nose.plugins.attrib import attr
 from opaque_keys.edx.locator import CourseLocator
 from six import text_type
 
@@ -32,6 +31,7 @@ from lms.djangoapps.instructor.enrollment import (
     unenroll_email
 )
 from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, get_mock_request
+from openedx.core.lib.tests import attr
 from student.models import CourseEnrollment, CourseEnrollmentAllowed, anonymous_id_for_user
 from student.roles import CourseCcxCoachRole
 from student.tests.factories import AdminFactory, UserFactory
diff --git a/lms/djangoapps/instructor/tests/test_proctoring.py b/lms/djangoapps/instructor/tests/test_proctoring.py
index a7746dffcfd..d5ffd543b37 100644
--- a/lms/djangoapps/instructor/tests/test_proctoring.py
+++ b/lms/djangoapps/instructor/tests/test_proctoring.py
@@ -6,7 +6,6 @@ import ddt
 from django.conf import settings
 from django.urls import reverse
 from mock import patch
-from nose.plugins.attrib import attr
 from six import text_type
 
 from student.roles import CourseStaffRole, CourseInstructorRole
@@ -15,13 +14,14 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 @patch.dict(settings.FEATURES, {'ENABLE_SPECIAL_EXAMS': True})
 @ddt.ddt
 class TestProctoringDashboardViews(SharedModuleStoreTestCase):
     """
     Check for Proctoring view on the new instructor dashboard
     """
+    shard = 1
+
     @classmethod
     def setUpClass(cls):
         super(TestProctoringDashboardViews, cls).setUpClass()
diff --git a/lms/djangoapps/instructor/tests/test_registration_codes.py b/lms/djangoapps/instructor/tests/test_registration_codes.py
index 52c85afdbdf..7a82c6d5486 100644
--- a/lms/djangoapps/instructor/tests/test_registration_codes.py
+++ b/lms/djangoapps/instructor/tests/test_registration_codes.py
@@ -6,7 +6,6 @@ import json
 from django.urls import reverse
 from django.test.utils import override_settings
 from django.utils.translation import ugettext as _
-from nose.plugins.attrib import attr
 from six import text_type
 
 from course_modes.models import CourseMode
@@ -27,12 +26,13 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 @override_settings(REGISTRATION_CODE_LENGTH=8)
 class TestCourseRegistrationCodeStatus(SharedModuleStoreTestCase):
     """
     Test registration code status.
     """
+    shard = 1
+
     @classmethod
     def setUpClass(cls):
         super(TestCourseRegistrationCodeStatus, cls).setUpClass()
diff --git a/lms/djangoapps/instructor/tests/test_services.py b/lms/djangoapps/instructor/tests/test_services.py
index b46b7f831a8..f52d1da434a 100644
--- a/lms/djangoapps/instructor/tests/test_services.py
+++ b/lms/djangoapps/instructor/tests/test_services.py
@@ -5,7 +5,6 @@ Tests for the InstructorService
 import json
 
 import mock
-from nose.plugins.attrib import attr
 
 from courseware.models import StudentModule
 from lms.djangoapps.instructor.access import allow_access
@@ -17,11 +16,12 @@ from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=1)
 class InstructorServiceTests(SharedModuleStoreTestCase):
     """
     Tests for the InstructorService
     """
+    shard = 1
+
     @classmethod
     def setUpClass(cls):
         super(InstructorServiceTests, cls).setUpClass()
diff --git a/lms/djangoapps/instructor/tests/test_spoc_gradebook.py b/lms/djangoapps/instructor/tests/test_spoc_gradebook.py
index df0110c8656..0950571b1af 100644
--- a/lms/djangoapps/instructor/tests/test_spoc_gradebook.py
+++ b/lms/djangoapps/instructor/tests/test_spoc_gradebook.py
@@ -3,12 +3,12 @@ Tests of the instructor dashboard spoc gradebook
 """
 
 from django.urls import reverse
-from nose.plugins.attrib import attr
 from six import text_type
 
 from capa.tests.response_xml_factory import StringResponseXMLFactory
 from courseware.tests.factories import StudentModuleFactory
 from lms.djangoapps.grades.tasks import compute_all_grades_for_course
+from openedx.core.lib.tests import attr
 from student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
 from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
diff --git a/lms/djangoapps/instructor/tests/test_tools.py b/lms/djangoapps/instructor/tests/test_tools.py
index 30c93bcdf9f..5b67c549d81 100644
--- a/lms/djangoapps/instructor/tests/test_tools.py
+++ b/lms/djangoapps/instructor/tests/test_tools.py
@@ -13,12 +13,12 @@ from django.core.exceptions import MultipleObjectsReturned
 from django.test import TestCase
 from django.test.utils import override_settings
 from pytz import UTC
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 from six import text_type
 
 from courseware.field_overrides import OverrideFieldData
 from lms.djangoapps.ccx.tests.test_overrides import inject_field_overrides
+from openedx.core.lib.tests import attr
 from student.tests.factories import UserFactory
 from xmodule.fields import Date
 from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
index cfc8045381e..5d2e62d1373 100644
--- a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
+++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py
@@ -10,7 +10,6 @@ from django.urls import reverse
 from django.test.client import RequestFactory
 from django.test.utils import override_settings
 from mock import patch
-from nose.plugins.attrib import attr
 from pytz import UTC
 from six import text_type
 
@@ -46,12 +45,12 @@ def intercept_renderer(path, context):
     return response
 
 
-@attr(shard=3)
 @ddt.ddt
 class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssTestMixin):
     """
     Tests for the instructor dashboard (not legacy).
     """
+    shard = 3
 
     def setUp(self):
         """
diff --git a/lms/djangoapps/lti_provider/tests/test_views.py b/lms/djangoapps/lti_provider/tests/test_views.py
index 4ce848ff89b..c1b0ab6e7e9 100644
--- a/lms/djangoapps/lti_provider/tests/test_views.py
+++ b/lms/djangoapps/lti_provider/tests/test_views.py
@@ -2,12 +2,10 @@
 Tests for the LTI provider views
 """
 
-import pytest
 from django.urls import reverse
 from django.test import TestCase
 from django.test.client import RequestFactory
 from mock import MagicMock, patch
-from nose.plugins.attrib import attr
 from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
 
 from courseware.testutils import RenderXBlockTestMixin
@@ -181,7 +179,6 @@ class LtiLaunchTest(LtiTestMixin, TestCase):
         self.assertEqual(consumer.instance_guid, u'consumer instance guid')
 
 
-@attr(shard=3)
 class LtiLaunchTestRender(LtiTestMixin, RenderXBlockTestMixin, ModuleStoreTestCase):
     """
     Tests for the rendering returned by lti_launch view.
@@ -189,6 +186,7 @@ class LtiLaunchTestRender(LtiTestMixin, RenderXBlockTestMixin, ModuleStoreTestCa
     the tests defined in RenderXBlockTestMixin.
     """
     SUCCESS_ENROLLED_STAFF_MONGO_COUNT = 9
+    shard = 3
 
     def get_response(self, usage_key, url_encoded_params=None):
         """
diff --git a/lms/djangoapps/mobile_api/course_info/tests.py b/lms/djangoapps/mobile_api/course_info/tests.py
index 0ae5aa52cd3..f0777c94f0d 100644
--- a/lms/djangoapps/mobile_api/course_info/tests.py
+++ b/lms/djangoapps/mobile_api/course_info/tests.py
@@ -5,7 +5,6 @@ Tests for course_info
 import ddt
 from django.conf import settings
 from milestones.tests.utils import MilestonesTestCaseMixin
-from nose.plugins.attrib import attr
 
 from xmodule.html_module import CourseInfoModule
 from xmodule.modulestore import ModuleStoreEnum
@@ -15,13 +14,13 @@ from xmodule.modulestore.xml_importer import import_course_from_xml
 from ..testutils import MobileAPITestCase, MobileAuthTestMixin, MobileCourseAccessTestMixin
 
 
-@attr(shard=3)
 @ddt.ddt
 class TestUpdates(MobileAPITestCase, MobileAuthTestMixin, MobileCourseAccessTestMixin, MilestonesTestCaseMixin):
     """
     Tests for /api/mobile/v0.5/course_info/{course_id}/updates
     """
     REVERSE_INFO = {'name': 'course-updates-list', 'params': ['course_id']}
+    shard = 3
 
     def verify_success(self, response):
         super(TestUpdates, self).verify_success(response)
@@ -83,13 +82,13 @@ class TestUpdates(MobileAPITestCase, MobileAuthTestMixin, MobileCourseAccessTest
             self.assertIn("Update" + str(num), update_data['content'])
 
 
-@attr(shard=3)
 @ddt.ddt
 class TestHandouts(MobileAPITestCase, MobileAuthTestMixin, MobileCourseAccessTestMixin, MilestonesTestCaseMixin):
     """
     Tests for /api/mobile/v0.5/course_info/{course_id}/handouts
     """
     REVERSE_INFO = {'name': 'course-handouts-list', 'params': ['course_id']}
+    shard = 3
 
     @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
     def test_handouts(self, default_ms):
diff --git a/lms/djangoapps/mobile_api/users/tests.py b/lms/djangoapps/mobile_api/users/tests.py
index 690b64d050f..f19d00cf1a4 100644
--- a/lms/djangoapps/mobile_api/users/tests.py
+++ b/lms/djangoapps/mobile_api/users/tests.py
@@ -12,7 +12,6 @@ from django.test import RequestFactory, override_settings
 from django.utils import timezone
 from milestones.tests.utils import MilestonesTestCaseMixin
 from mock import patch
-from nose.plugins.attrib import attr
 
 from lms.djangoapps.certificates.api import generate_user_certificates
 from lms.djangoapps.certificates.models import CertificateStatuses
@@ -27,6 +26,7 @@ from mobile_api.testutils import (
     MobileCourseAccessTestMixin
 )
 from openedx.core.lib.courses import course_image_url
+from openedx.core.lib.tests import attr
 from student.models import CourseEnrollment
 from util.milestones_helpers import set_prerequisite_courses
 from util.testing import UrlResetMixin
diff --git a/lms/djangoapps/mobile_api/video_outlines/tests.py b/lms/djangoapps/mobile_api/video_outlines/tests.py
index caf0baf3240..2cb59a35602 100644
--- a/lms/djangoapps/mobile_api/video_outlines/tests.py
+++ b/lms/djangoapps/mobile_api/video_outlines/tests.py
@@ -14,13 +14,13 @@ from django.conf import settings
 from edxval import api
 from milestones.tests.utils import MilestonesTestCaseMixin
 from mock import patch
-from nose.plugins.attrib import attr
 
 from mobile_api.models import MobileApiConfig
 from mobile_api.testutils import MobileAPITestCase, MobileAuthTestMixin, MobileCourseAccessTestMixin
 from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, remove_user_from_cohort
 from openedx.core.djangoapps.course_groups.models import CourseUserGroupPartitionGroup
 from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory
+from openedx.core.lib.tests import attr
 from xmodule.modulestore.django import modulestore
 from xmodule.modulestore.tests.factories import ItemFactory
 from xmodule.partitions.partitions import Group, UserPartition
diff --git a/lms/djangoapps/shoppingcart/tests/test_models.py b/lms/djangoapps/shoppingcart/tests/test_models.py
index b64f786162e..b4028033c70 100644
--- a/lms/djangoapps/shoppingcart/tests/test_models.py
+++ b/lms/djangoapps/shoppingcart/tests/test_models.py
@@ -20,7 +20,6 @@ from django.db import DatabaseError
 from django.test import TestCase
 from django.test.utils import override_settings
 from mock import Mock, MagicMock, patch
-from nose.plugins.attrib import attr
 from opaque_keys.edx.locator import CourseLocator
 
 from course_modes.models import CourseMode
@@ -56,13 +55,13 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
-@attr(shard=3)
 @ddt.ddt
 class OrderTest(ModuleStoreTestCase):
     """
     Test shopping cart orders (e.g., cart contains various items,
     order is taken through various pieces of cart state, etc.)
     """
+    shard = 3
 
     def setUp(self):
         super(OrderTest, self).setUp()
@@ -489,12 +488,13 @@ class OrderItemTest(TestCase):
         self.assertEqual(item.get_list_price(), item.list_price)
 
 
-@attr(shard=3)
 @patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
 class PaidCourseRegistrationTest(ModuleStoreTestCase):
     """
     Paid Course Registration Tests.
     """
+    shard = 3
+
     def setUp(self):
         super(PaidCourseRegistrationTest, self).setUp()
 
diff --git a/lms/djangoapps/shoppingcart/tests/test_views.py b/lms/djangoapps/shoppingcart/tests/test_views.py
index ad34161f521..e60e573e130 100644
--- a/lms/djangoapps/shoppingcart/tests/test_views.py
+++ b/lms/djangoapps/shoppingcart/tests/test_views.py
@@ -21,7 +21,6 @@ from django.test import TestCase
 from django.test.utils import override_settings
 from freezegun import freeze_time
 from mock import Mock, patch
-from nose.plugins.attrib import attr
 from pytz import UTC
 from six import text_type
 
@@ -70,13 +69,13 @@ render_mock = Mock(side_effect=mock_render_to_response)
 postpay_mock = Mock()
 
 
-@attr(shard=3)
 @patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
 @ddt.ddt
 class ShoppingCartViewsTests(SharedModuleStoreTestCase, XssTestMixin):
     """
     Test shopping cart view under various states
     """
+    shard = 3
 
     @classmethod
     def setUpClass(cls):
diff --git a/lms/djangoapps/student_account/test/test_views.py b/lms/djangoapps/student_account/test/test_views.py
index 3a50f11c54e..ed3380ed2eb 100644
--- a/lms/djangoapps/student_account/test/test_views.py
+++ b/lms/djangoapps/student_account/test/test_views.py
@@ -8,7 +8,6 @@ from urllib import urlencode
 
 import ddt
 import mock
-import pytest
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth import get_user_model
@@ -26,7 +25,6 @@ from django.utils.translation import ugettext as _
 from edx_oauth2_provider.tests.factories import AccessTokenFactory, ClientFactory, RefreshTokenFactory
 from edx_rest_api_client import exceptions
 from http.cookies import SimpleCookie
-from nose.plugins.attrib import attr
 from oauth2_provider.models import AccessToken as dot_access_token
 from oauth2_provider.models import RefreshToken as dot_refresh_token
 from provider.oauth2.models import AccessToken as dop_access_token
@@ -295,11 +293,10 @@ class StudentAccountUpdateTest(CacheIsolationTestCase, UrlResetMixin):
         self.assertFalse(dop_refresh_token.objects.filter(user=user).exists())
 
 
-@attr(shard=7)
 @ddt.ddt
 class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMixin, ModuleStoreTestCase):
     """ Tests for the student account views that update the user's account information. """
-
+    shard = 7
     USERNAME = "bob"
     EMAIL = "bob@example.com"
     PASSWORD = "password"
diff --git a/lms/djangoapps/support/tests/test_views.py b/lms/djangoapps/support/tests/test_views.py
index 50748db69dc..e8194e4976d 100644
--- a/lms/djangoapps/support/tests/test_views.py
+++ b/lms/djangoapps/support/tests/test_views.py
@@ -9,11 +9,9 @@ import re
 from datetime import datetime, timedelta
 
 import ddt
-import pytest
 from django.contrib.auth.models import User
 from django.urls import reverse
 from django.db.models import signals
-from nose.plugins.attrib import attr
 from pytz import UTC
 
 from common.test.utils import disable_signal
@@ -90,12 +88,12 @@ class SupportViewManageUserTests(SupportViewTestCase):
         self.assertEqual(test_user.has_usable_password(), False)
 
 
-@attr(shard=3)
 @ddt.ddt
 class SupportViewAccessTests(SupportViewTestCase):
     """
     Tests for access control of support views.
     """
+    shard = 3
 
     @ddt.data(*(
         (url_name, role, has_access)
diff --git a/lms/djangoapps/teams/tests/test_views.py b/lms/djangoapps/teams/tests/test_views.py
index f6717a9b7d5..adfa1fe3f65 100644
--- a/lms/djangoapps/teams/tests/test_views.py
+++ b/lms/djangoapps/teams/tests/test_views.py
@@ -13,7 +13,6 @@ from django.db.models.signals import post_save
 from django.utils import translation
 from elasticsearch.exceptions import ConnectionError
 from mock import patch
-from nose.plugins.attrib import attr
 from rest_framework.test import APIClient, APITestCase
 from search.search_engine_base import SearchEngine
 
@@ -32,7 +31,6 @@ from ..search_indexes import CourseTeam, CourseTeamIndexer, course_team_post_sav
 from .factories import LAST_ACTIVITY_AT, CourseTeamFactory
 
 
-@attr(shard=1)
 class TestDashboard(SharedModuleStoreTestCase):
     """Tests for the Teams dashboard."""
     shard = 6
diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py
index d9c1416c718..a7013c59ad1 100644
--- a/lms/djangoapps/verify_student/tests/test_views.py
+++ b/lms/djangoapps/verify_student/tests/test_views.py
@@ -24,7 +24,6 @@ from django.test.client import Client, RequestFactory
 from django.test.utils import override_settings
 from django.utils.translation import ugettext as _
 from mock import Mock, patch
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 from opaque_keys.edx.locator import CourseLocator
 from waffle.testutils import override_switch
@@ -45,6 +44,7 @@ from lms.djangoapps.verify_student.views import (
 from openedx.core.djangoapps.embargo.test_utils import restrict_course
 from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
 from openedx.core.djangoapps.user_api.accounts.api import get_account_settings
+from openedx.core.lib.tests import attr
 from shoppingcart.models import CertificateItem, Order
 from student.models import CourseEnrollment
 from student.tests.factories import CourseEnrollmentFactory, UserFactory
diff --git a/scripts/thresholds.sh b/scripts/thresholds.sh
index 94b318ffd4e..de0bf9955aa 100755
--- a/scripts/thresholds.sh
+++ b/scripts/thresholds.sh
@@ -2,6 +2,6 @@
 set -e
 
 export LOWER_PYLINT_THRESHOLD=1000
-export UPPER_PYLINT_THRESHOLD=3965
+export UPPER_PYLINT_THRESHOLD=3875
 export ESLINT_THRESHOLD=5590
 export STYLELINT_THRESHOLD=973
-- 
GitLab