diff --git a/common/djangoapps/enrollment/tests/test_emails.py b/common/djangoapps/enrollment/tests/test_emails.py
index 7e42aaeb39c78449fe89076bc69455389ea38c38..57b3bda61a39a7481978b9fa90e89a11b82f9a35 100644
--- a/common/djangoapps/enrollment/tests/test_emails.py
+++ b/common/djangoapps/enrollment/tests/test_emails.py
@@ -6,7 +6,6 @@ import unittest
 from django.conf import settings
 from django.core import mail
 from django.test.utils import override_settings
-from nose.plugins.attrib import attr
 from rest_framework.test import APITestCase
 from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
@@ -18,7 +17,6 @@ from student.tests.factories import UserFactory
 from .test_views import EnrollmentTestMixin
 
 
-@attr(shard=3)
 @override_settings(EDX_API_KEY="i am a key")
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 @override_settings(ENROLLMENT_NOTIFICATION_EMAIL="some_admins@example.com")
@@ -35,6 +33,7 @@ class EnrollmentEmailNotificationTest(EnrollmentTestMixin,
 
     ENABLED_CACHES = ['default', 'mongo_metadata_inheritance', 'loc_cache']
     ENABLED_SIGNALS = ['course_published']
+    shard = 3
 
     def setUp(self):
         """ Create a course and user, then log in. Also creates a course mode."""
diff --git a/common/test/acceptance/performance/test_lms_performance.py b/common/test/acceptance/performance/test_lms_performance.py
index 53da963944575f5b6928379b9c10af1af5fe6049..49f9fe784caeedd5d3063acd157f1db9701ccaa3 100644
--- a/common/test/acceptance/performance/test_lms_performance.py
+++ b/common/test/acceptance/performance/test_lms_performance.py
@@ -2,7 +2,6 @@
 Single page performance tests for LMS.
 """
 from bok_choy.web_app_test import with_cache
-from nose.plugins.attrib import attr
 
 from common.test.acceptance.fixtures.course import CourseFixture, CourseUpdateDesc, XBlockFixtureDesc
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
@@ -15,13 +14,13 @@ from common.test.acceptance.pages.lms.progress import ProgressPage
 from common.test.acceptance.tests.helpers import UniqueCourseTest, load_data_str
 
 
-@attr(har_mode='explicit')
 class LmsPerformanceTest(UniqueCourseTest):
     """
     Base class to capture LMS performance with HTTP Archives.
     """
     username = 'test_student'
     email = 'student101@example.com'
+    har_mode = 'explicit'
 
     def setUp(self):
         """
diff --git a/common/test/acceptance/performance/test_studio_performance.py b/common/test/acceptance/performance/test_studio_performance.py
index 28811595ef54cc8e735c86c547ee878b06f15a8a..c2eb86e2dcef576e2f650069a659f9479f4a216c 100644
--- a/common/test/acceptance/performance/test_studio_performance.py
+++ b/common/test/acceptance/performance/test_studio_performance.py
@@ -2,7 +2,6 @@
 Single page performance tests for Studio.
 """
 from bok_choy.web_app_test import with_cache
-from nose.plugins.attrib import attr
 
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.studio.overview import CourseOutlinePage
@@ -10,7 +9,6 @@ from common.test.acceptance.pages.studio.overview import CourseOutlinePage
 from ..tests.helpers import AcceptanceTest
 
 
-@attr(har_mode='explicit')
 class StudioPagePerformanceTest(AcceptanceTest):
     """
     Base class to capture studio performance with HTTP Archives.
@@ -21,6 +19,7 @@ class StudioPagePerformanceTest(AcceptanceTest):
     course_org = 'edX'
     course_num = 'Open_DemoX'
     course_run = 'edx_demo_course'
+    har_mode = 'explicit'
 
     def setUp(self):
         """
diff --git a/common/test/acceptance/tests/lms/test_account_settings.py b/common/test/acceptance/tests/lms/test_account_settings.py
index d3b3c8c0c3a2b942b20f38756ac8bf7259f1cf1d..b353c631fbef4b5eb810b4f9710bd3956fe26bea 100644
--- a/common/test/acceptance/tests/lms/test_account_settings.py
+++ b/common/test/acceptance/tests/lms/test_account_settings.py
@@ -5,8 +5,8 @@ End-to-end tests for the Account Settings page.
 from datetime import datetime
 from unittest import skip
 
+import pytest
 from bok_choy.page_object import XSS_INJECTION
-from nose.plugins.attrib import attr
 from pytz import timezone, utc
 
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage, FULL_NAME
@@ -98,11 +98,12 @@ class AccountSettingsTestMixin(EventsTestMixin, AcceptanceTest):
         self.assert_no_matching_events_were_emitted({'event_type': self.USER_SETTINGS_CHANGED_EVENT_NAME})
 
 
-@attr(shard=8)
 class DashboardMenuTest(AccountSettingsTestMixin, AcceptanceTest):
     """
     Tests that the dashboard menu works correctly with the account settings page.
     """
+    shard = 8
+
     def test_link_on_dashboard_works(self):
         """
         Scenario: Verify that the "Account" link works from the dashboard.
@@ -121,12 +122,12 @@ class DashboardMenuTest(AccountSettingsTestMixin, AcceptanceTest):
         dashboard_page.click_account_settings_link()
 
 
-@attr(shard=8)
 class AccountSettingsPageTest(AccountSettingsTestMixin, AcceptanceTest):
     """
     Tests that verify behaviour of the Account Settings page.
     """
     SUCCESS_MESSAGE = 'Your changes have been saved.'
+    shard = 8
 
     def setUp(self):
         """
@@ -575,7 +576,7 @@ class AccountSettingsDeleteAccountTest(AccountSettingsTestMixin, AcceptanceTest)
         )
 
 
-@attr('a11y')
+@pytest.mark.a11y
 class AccountSettingsA11yTest(AccountSettingsTestMixin, AcceptanceTest):
     """
     Class to test account settings accessibility.
diff --git a/common/test/acceptance/tests/lms/test_bookmarks.py b/common/test/acceptance/tests/lms/test_bookmarks.py
index b6b994c868ec721cf577605f18cfa3623b3fd98b..1207b7c91a1c85ae126140e60a14874d49b1062f 100644
--- a/common/test/acceptance/tests/lms/test_bookmarks.py
+++ b/common/test/acceptance/tests/lms/test_bookmarks.py
@@ -5,8 +5,8 @@ End-to-end tests for the courseware unit bookmarks.
 import json
 from unittest import skip
 
+import pytest
 import requests
-from nose.plugins.attrib import attr
 
 from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
 from common.test.acceptance.pages.common import BASE_URL
@@ -127,11 +127,11 @@ class BookmarksTestMixin(EventsTestMixin, UniqueCourseTest):
             self._bookmark_unit(xblocks[index].locator)
 
 
-@attr(shard=8)
 class BookmarksTest(BookmarksTestMixin):
     """
     Tests to verify bookmarks functionality.
     """
+    shard = 8
 
     def _breadcrumb(self, num_units, modified_name=None):
         """
@@ -586,7 +586,7 @@ class BookmarksTest(BookmarksTestMixin):
         )
 
 
-@attr('a11y')
+@pytest.mark.a11y
 class BookmarksA11yTests(BookmarksTestMixin):
     """
     Tests for checking the a11y of the bookmarks page.
diff --git a/common/test/acceptance/tests/lms/test_ccx.py b/common/test/acceptance/tests/lms/test_ccx.py
index c7bb775c6e6c3b5aabc1078e74308de1519ec80b..3a5c4f4847d0bf95f78b8644b17e79415d3d3582 100644
--- a/common/test/acceptance/tests/lms/test_ccx.py
+++ b/common/test/acceptance/tests/lms/test_ccx.py
@@ -2,21 +2,19 @@
 """
 End-to-end tests for the CCX dashboard.
 """
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.fixtures.course import CourseFixture
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.lms.ccx_dashboard_page import CoachDashboardPage
 from common.test.acceptance.tests.helpers import EventsTestMixin, UniqueCourseTest
 
 
-@attr(shard=7)
 class CreateCCXCoachTest(EventsTestMixin, UniqueCourseTest):
     """
     Test ccx end to end process.
     """
     USERNAME = "coach_tester"
     EMAIL = "coach_tester@example.com"
+    shard = 7
 
     def setUp(self):
         super(CreateCCXCoachTest, self).setUp()
diff --git a/common/test/acceptance/tests/lms/test_certificate_web_view.py b/common/test/acceptance/tests/lms/test_certificate_web_view.py
index 807566495ab5346abd3a38863d721b0ce003eafc..b4f38a0b1374d522848f8b0ed329da18a43c1137 100644
--- a/common/test/acceptance/tests/lms/test_certificate_web_view.py
+++ b/common/test/acceptance/tests/lms/test_certificate_web_view.py
@@ -1,24 +1,18 @@
 """
 Acceptance tests for the certificate web view feature.
 """
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.fixtures.certificates import CertificateConfigFixture
-from common.test.acceptance.fixtures.course import CourseFixture, CourseUpdateDesc, XBlockFixtureDesc
+from common.test.acceptance.fixtures.course import CourseFixture
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.lms.certificate_page import CertificatePage
-from common.test.acceptance.pages.lms.course_home import CourseHomePage
-from common.test.acceptance.pages.lms.courseware import CoursewarePage
-from common.test.acceptance.pages.lms.progress import ProgressPage
-from common.test.acceptance.pages.lms.tab_nav import TabNavPage
-from common.test.acceptance.tests.helpers import EventsTestMixin, UniqueCourseTest, get_element_padding, load_data_str
+from common.test.acceptance.tests.helpers import EventsTestMixin, UniqueCourseTest
 
 
-@attr(shard=5)
 class CertificateWebViewTest(EventsTestMixin, UniqueCourseTest):
     """
     Tests for verifying certificate web view features
     """
+    shard = 5
 
     def setUp(self):
         super(CertificateWebViewTest, self).setUp()
diff --git a/common/test/acceptance/tests/lms/test_learner_profile.py b/common/test/acceptance/tests/lms/test_learner_profile.py
index f57a315597bb33fa1db954957d9470679e44b58e..69f45a036ddb313401584bb6819d2ba0c988de8c 100644
--- a/common/test/acceptance/tests/lms/test_learner_profile.py
+++ b/common/test/acceptance/tests/lms/test_learner_profile.py
@@ -6,7 +6,7 @@ from contextlib import contextmanager
 from datetime import datetime
 from unittest import skip
 
-from nose.plugins.attrib import attr
+import pytest
 
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.common.logout import LogoutPage
@@ -182,11 +182,11 @@ class LearnerProfileTestMixin(EventsTestMixin):
         return username, user_id
 
 
-@attr(shard=4)
 class OwnLearnerProfilePageTest(LearnerProfileTestMixin, AcceptanceTest):
     """
     Tests that verify a student's own profile page.
     """
+    shard = 4
 
     def verify_profile_forced_private_message(self, username, birth_year, message=None):
         """
@@ -611,11 +611,12 @@ class OwnLearnerProfilePageTest(LearnerProfileTestMixin, AcceptanceTest):
             profile_page.upload_file(filename='image.jpg', wait_for_upload_button=False)
 
 
-@attr(shard=4)
 class DifferentUserLearnerProfilePageTest(LearnerProfileTestMixin, AcceptanceTest):
     """
     Tests that verify viewing the profile page of a different user.
     """
+    shard = 4
+
     def test_different_user_private_profile(self):
         """
         Scenario: Verify that desired fields are shown when looking at a different user's private profile.
@@ -678,7 +679,7 @@ class DifferentUserLearnerProfilePageTest(LearnerProfileTestMixin, AcceptanceTes
         badge.close_modal()
 
 
-@attr('a11y')
+@pytest.mark.a11y
 class LearnerProfileA11yTest(LearnerProfileTestMixin, AcceptanceTest):
     """
     Class to test learner profile accessibility.
diff --git a/common/test/acceptance/tests/lms/test_library.py b/common/test/acceptance/tests/lms/test_library.py
index e94d27ccfa2a9ec14bf8d32636cc3f96ccddfcc5..972dbe427c6cc2953d5aeca7c360d036dc80ba73 100644
--- a/common/test/acceptance/tests/lms/test_library.py
+++ b/common/test/acceptance/tests/lms/test_library.py
@@ -5,7 +5,6 @@ End-to-end tests for LibraryContent block in LMS
 import textwrap
 
 import ddt
-from nose.plugins.attrib import attr
 
 from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
 from common.test.acceptance.fixtures.library import LibraryFixture
@@ -23,7 +22,6 @@ SUBSECTION_NAME = 'Test Subsection'
 UNIT_NAME = 'Test Unit'
 
 
-@attr(shard=10)
 class LibraryContentTestBase(UniqueCourseTest):
     """ Base class for library content block tests """
     USERNAME = "STUDENT_TESTER"
@@ -31,6 +29,7 @@ class LibraryContentTestBase(UniqueCourseTest):
 
     STAFF_USERNAME = "STAFF_TESTER"
     STAFF_EMAIL = "staff101@example.com"
+    shard = 10
 
     def populate_library_fixture(self, library_fixture):
         """
@@ -147,11 +146,12 @@ class LibraryContentTestBase(UniqueCourseTest):
 
 
 @ddt.ddt
-@attr(shard=10)
 class LibraryContentTest(LibraryContentTestBase):
     """
     Test courseware.
     """
+    shard = 10
+
     def populate_library_fixture(self, library_fixture):
         """
         Populates library fixture with XBlock Fixtures
@@ -200,11 +200,12 @@ class LibraryContentTest(LibraryContentTestBase):
 
 
 @ddt.ddt
-@attr(shard=10)
 class StudioLibraryContainerCapaFilterTest(LibraryContentTestBase, TestWithSearchIndexMixin):
     """
     Test Library Content block in LMS
     """
+    shard = 10
+
     def setUp(self):
         """ SetUp method """
         self._create_search_index()
diff --git a/common/test/acceptance/tests/lms/test_lms_cohorted_courseware_search.py b/common/test/acceptance/tests/lms/test_lms_cohorted_courseware_search.py
index 62e477fbcfb728c36de13d6dddf47a02f64f8e50..207a891d45e8172551c0ebadacc23929475ad7d0 100644
--- a/common/test/acceptance/tests/lms/test_lms_cohorted_courseware_search.py
+++ b/common/test/acceptance/tests/lms/test_lms_cohorted_courseware_search.py
@@ -5,8 +5,6 @@ Test courseware search
 import json
 import uuid
 
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.fixtures.course import XBlockFixtureDesc
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.common.logout import LogoutPage
@@ -21,12 +19,11 @@ from common.test.acceptance.tests.helpers import remove_file
 from common.test.acceptance.tests.studio.base_studio_test import ContainerBase
 
 
-@attr(shard=1)
 class CoursewareSearchCohortTest(ContainerBase, CohortTestMixin):
     """
     Test courseware search.
     """
-
+    shard = 1
     TEST_INDEX_FILENAME = "test_root/index_file.dat"
 
     def setUp(self, is_staff=True):
diff --git a/common/test/acceptance/tests/lms/test_lms_courseware.py b/common/test/acceptance/tests/lms/test_lms_courseware.py
index d2f65cd6cf2513ded108beb4936420c47e5e1813..457b869b7cdad96609483bfce75d73cb086fde29 100644
--- a/common/test/acceptance/tests/lms/test_lms_courseware.py
+++ b/common/test/acceptance/tests/lms/test_lms_courseware.py
@@ -7,7 +7,8 @@ import json
 from datetime import datetime, timedelta
 
 import ddt
-from nose.plugins.attrib import attr
+
+from openedx.core.lib.tests import attr
 
 from ...fixtures.course import CourseFixture, XBlockFixtureDesc
 from ...pages.common.auto_auth import AutoAuthPage
@@ -19,7 +20,6 @@ from ...pages.lms.dashboard import DashboardPage
 from ...pages.lms.pay_and_verify import FakePaymentPage, FakeSoftwareSecureVerificationPage, PaymentAndVerificationFlow
 from ...pages.lms.problem import ProblemPage
 from ...pages.lms.progress import ProgressPage
-from ...pages.lms.staff_view import StaffCoursewarePage
 from ...pages.lms.track_selection import TrackSelectionPage
 from ...pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
 from ..helpers import EventsTestMixin, UniqueCourseTest, auto_auth, create_multiple_choice_problem
diff --git a/common/test/acceptance/tests/lms/test_lms_courseware_search.py b/common/test/acceptance/tests/lms/test_lms_courseware_search.py
index ccc9fd8430e1e88b1dd77135b1314ab87d14c78c..d22f712cbf3f21f4e0cf8f92500fff249a64f670 100644
--- a/common/test/acceptance/tests/lms/test_lms_courseware_search.py
+++ b/common/test/acceptance/tests/lms/test_lms_courseware_search.py
@@ -3,8 +3,6 @@ Test courseware search
 """
 import json
 
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.common.logout import LogoutPage
@@ -17,7 +15,6 @@ from common.test.acceptance.pages.studio.utils import add_html_component, type_i
 from common.test.acceptance.tests.helpers import UniqueCourseTest, remove_file
 
 
-@attr(shard=5)
 class CoursewareSearchTest(UniqueCourseTest):
     """
     Test courseware search.
@@ -41,6 +38,7 @@ class CoursewareSearchTest(UniqueCourseTest):
     EDITED_SEARCH_STRING = "edited"
 
     TEST_INDEX_FILENAME = "test_root/index_file.dat"
+    shard = 5
 
     def setUp(self):
         """
diff --git a/common/test/acceptance/tests/lms/test_lms_dashboard.py b/common/test/acceptance/tests/lms/test_lms_dashboard.py
index 15014e4b02885389bd3edd636b54127ba0f8b446..8786380a214a4e5052f74d4ba25f706116ccc28f 100644
--- a/common/test/acceptance/tests/lms/test_lms_dashboard.py
+++ b/common/test/acceptance/tests/lms/test_lms_dashboard.py
@@ -4,7 +4,7 @@ End-to-end tests for the main LMS Dashboard (aka, Student Dashboard).
 """
 import datetime
 
-from nose.plugins.attrib import attr
+import pytest
 
 from common.test.acceptance.fixtures.course import CourseFixture
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
@@ -141,9 +141,9 @@ class BaseLmsDashboardTestMultiple(UniqueCourseTest):
         self.dashboard_page.visit()
 
 
-@attr(shard=9)
 class LmsDashboardPageTest(BaseLmsDashboardTest):
     """ Test suite for the LMS Student Dashboard page """
+    shard = 9
 
     def setUp(self):
         super(LmsDashboardPageTest, self).setUp()
@@ -399,7 +399,7 @@ class LmsDashboardCourseUnEnrollDialogMessageTest(BaseLmsDashboardTestMultiple):
         self.assertEqual(dialog_message['refund-info'][0], expected_refund_message)
 
 
-@attr('a11y')
+@pytest.mark.a11y
 class LmsDashboardA11yTest(BaseLmsDashboardTestMultiple):
     """
     Class to test lms student dashboard accessibility.
diff --git a/common/test/acceptance/tests/lms/test_lms_edxnotes.py b/common/test/acceptance/tests/lms/test_lms_edxnotes.py
index 8a0567c228ec2c4ef6d089b4f9735dc8b5ff1366..0cebfdd843f01b0fe901e5583438db61487e3bfa 100644
--- a/common/test/acceptance/tests/lms/test_lms_edxnotes.py
+++ b/common/test/acceptance/tests/lms/test_lms_edxnotes.py
@@ -6,8 +6,6 @@ from datetime import datetime
 from unittest import skip
 from uuid import uuid4
 
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
 from common.test.acceptance.fixtures.edxnotes import EdxNotesFixture, Note, Range
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
@@ -15,6 +13,7 @@ from common.test.acceptance.pages.lms.course_home import CourseHomePage
 from common.test.acceptance.pages.lms.courseware import CoursewarePage
 from common.test.acceptance.pages.lms.edxnotes import EdxNotesPage, EdxNotesPageNoContent, EdxNotesUnitPage
 from common.test.acceptance.tests.helpers import EventsTestMixin, UniqueCourseTest
+from openedx.core.lib.tests import attr
 
 
 class EdxNotesTestMixin(UniqueCourseTest):
diff --git a/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py b/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py
index d810bdb271226b258aaf9ee5c2fa761ffa13e42c..6054db3aa2b6c4bfded475ed63a627486dc50abb 100644
--- a/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py
+++ b/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py
@@ -5,7 +5,6 @@ End-to-end tests for the LMS Instructor Dashboard.
 
 import ddt
 from bok_choy.promise import EmptyPromise
-from nose.plugins.attrib import attr
 
 from common.test.acceptance.fixtures.certificates import CertificateConfigFixture
 from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
@@ -31,6 +30,7 @@ from common.test.acceptance.tests.helpers import (
     disable_animations,
     get_modal_alert
 )
+from openedx.core.lib.tests import attr
 
 
 class BaseInstructorDashboardTest(EventsTestMixin, UniqueCourseTest):
diff --git a/common/test/acceptance/tests/lms/test_lms_problems.py b/common/test/acceptance/tests/lms/test_lms_problems.py
index 7e14c2650f767be07cf75929e1ad27153586328b..d9fa2949fef29d0e3f86d364f9e4d3c20d76f040 100644
--- a/common/test/acceptance/tests/lms/test_lms_problems.py
+++ b/common/test/acceptance/tests/lms/test_lms_problems.py
@@ -6,14 +6,13 @@ See also old lettuce tests in lms/djangoapps/courseware/features/problems.featur
 """
 from textwrap import dedent
 
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.lms.courseware import CoursewarePage
 from common.test.acceptance.pages.lms.login_and_register import CombinedLoginAndRegisterPage
 from common.test.acceptance.pages.lms.problem import ProblemPage
 from common.test.acceptance.tests.helpers import EventsTestMixin, UniqueCourseTest
+from openedx.core.lib.tests import attr
 
 
 class ProblemsTest(UniqueCourseTest):
diff --git a/common/test/acceptance/tests/lms/test_lms_user_preview.py b/common/test/acceptance/tests/lms/test_lms_user_preview.py
index 378a5db7a7ece927066e539bb68950d8226627cd..53c86a08b8fa6e2d52c97f69f3228b6a718013f9 100644
--- a/common/test/acceptance/tests/lms/test_lms_user_preview.py
+++ b/common/test/acceptance/tests/lms/test_lms_user_preview.py
@@ -3,17 +3,15 @@
 Tests the "preview" selector in the LMS that allows changing between Staff, Learner, and Content Groups.
 """
 
-
 from textwrap import dedent
 
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.lms.courseware import CoursewarePage
 from common.test.acceptance.pages.lms.instructor_dashboard import InstructorDashboardPage
 from common.test.acceptance.pages.lms.staff_view import StaffCoursewarePage
 from common.test.acceptance.tests.helpers import UniqueCourseTest, create_user_partition_json
+from openedx.core.lib.tests import attr
 from xmodule.partitions.partitions import ENROLLMENT_TRACK_PARTITION_ID, MINIMUM_STATIC_PARTITION_ID, Group
 
 
diff --git a/common/test/acceptance/tests/lms/test_programs.py b/common/test/acceptance/tests/lms/test_programs.py
index 2ba18994d0ec397e3b99fbc6210951de9d9826e9..0495e3df67415dd7284a1b3424fa85d4c888d710 100644
--- a/common/test/acceptance/tests/lms/test_programs.py
+++ b/common/test/acceptance/tests/lms/test_programs.py
@@ -1,5 +1,5 @@
 """Acceptance tests for LMS-hosted Programs pages"""
-from nose.plugins.attrib import attr
+import pytest
 
 from common.test.acceptance.fixtures.catalog import CatalogFixture, CatalogIntegrationMixin
 from common.test.acceptance.fixtures.course import CourseFixture
@@ -74,9 +74,10 @@ class ProgramPageBase(ProgramsConfigMixin, CatalogIntegrationMixin, UniqueCourse
         cache_programs_page.visit()
 
 
-@attr(shard=21)
 class ProgramListingPageTest(ProgramPageBase):
     """Verify user-facing behavior of the program listing page."""
+    shard = 21
+
     def setUp(self):
         super(ProgramListingPageTest, self).setUp()
 
@@ -108,7 +109,7 @@ class ProgramListingPageTest(ProgramPageBase):
         self.assertFalse(self.listing_page.are_cards_present)
 
 
-@attr('a11y')
+@pytest.mark.a11y
 class ProgramListingPageA11yTest(ProgramPageBase):
     """Test program listing page accessibility."""
     def setUp(self):
@@ -143,7 +144,7 @@ class ProgramListingPageA11yTest(ProgramPageBase):
         self.listing_page.a11y_audit.check_for_accessibility_errors()
 
 
-@attr('a11y')
+@pytest.mark.a11y
 class ProgramDetailsPageA11yTest(ProgramPageBase):
     """Test program details page accessibility."""
     def setUp(self):
diff --git a/common/test/acceptance/tests/lms/test_progress_page.py b/common/test/acceptance/tests/lms/test_progress_page.py
index a7972c7843f4d01780d91d5ec254e3cef8ac662d..9d0e3ea910a1ef9f6199793db7bd55c44bfc2511 100644
--- a/common/test/acceptance/tests/lms/test_progress_page.py
+++ b/common/test/acceptance/tests/lms/test_progress_page.py
@@ -6,7 +6,7 @@ progress page.
 from contextlib import contextmanager
 
 import ddt
-from nose.plugins.attrib import attr
+import pytest
 
 from ...fixtures.course import CourseFixture, XBlockFixtureDesc
 from ...pages.common.logout import LogoutPage
@@ -127,13 +127,14 @@ class ProgressPageBaseTest(UniqueCourseTest):
             self.logout_page.visit()
 
 
-@attr(shard=22)
 @ddt.ddt
 class PersistentGradesTest(ProgressPageBaseTest):
     """
     Test that grades for completed assessments are persisted
     when various edits are made.
     """
+    shard = 22
+
     def setUp(self):
         super(PersistentGradesTest, self).setUp()
         self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
@@ -275,12 +276,13 @@ class PersistentGradesTest(ProgressPageBaseTest):
             self.assertEqual(self._get_section_score(), (0, 2))
 
 
-@attr(shard=22)
 class SubsectionGradingPolicyTest(ProgressPageBaseTest):
     """
     Tests changing a subsection's 'graded' field
     and the effect it has on the progress page.
     """
+    shard = 22
+
     def setUp(self):
         super(SubsectionGradingPolicyTest, self).setUp()
         self._set_policy_for_subsection("Homework", 0)
@@ -417,7 +419,7 @@ class SubsectionGradingPolicyTest(ProgressPageBaseTest):
             self._check_scores_and_page_text([(1, 1), (0, 1)], (1, 2), "Homework 1 - Test Subsection 1 - 50% (1/2)")
 
 
-@attr('a11y')
+@pytest.mark.a11y
 class ProgressPageA11yTest(ProgressPageBaseTest):
     """
     Class to test the accessibility of the progress page.
diff --git a/common/test/acceptance/tests/lms/test_teams.py b/common/test/acceptance/tests/lms/test_teams.py
index 99fd4cbdb6108c1aacb6705f274dd7e0f33ec98c..1e047840bf3b070ce56d75a89da3d18b7beb35c6 100644
--- a/common/test/acceptance/tests/lms/test_teams.py
+++ b/common/test/acceptance/tests/lms/test_teams.py
@@ -8,7 +8,6 @@ from uuid import uuid4
 
 import ddt
 from dateutil.parser import parse
-from nose.plugins.attrib import attr
 from selenium.common.exceptions import TimeoutException
 
 from common.test.acceptance.fixtures import LMS_BASE_URL
@@ -29,6 +28,7 @@ from common.test.acceptance.pages.lms.teams import (
     TeamsPage
 )
 from common.test.acceptance.tests.helpers import EventsTestMixin, UniqueCourseTest, get_modal_alert
+from openedx.core.lib.tests import attr
 
 TOPICS_PER_PAGE = 12
 
diff --git a/common/test/acceptance/tests/studio/test_import_export.py b/common/test/acceptance/tests/studio/test_import_export.py
index d6837bf75244ecda5f5ae89e99b1ba7a17fa2f87..7a047503acfaabb3832a9daeb691646a728d39dd 100644
--- a/common/test/acceptance/tests/studio/test_import_export.py
+++ b/common/test/acceptance/tests/studio/test_import_export.py
@@ -4,11 +4,6 @@ Acceptance tests for the Import and Export pages
 from abc import abstractmethod
 from datetime import datetime
 
-from nose.plugins.attrib import attr
-
-from common.test.acceptance.pages.lms.course_home import CourseHomePage
-from common.test.acceptance.pages.lms.courseware import CoursewarePage
-from common.test.acceptance.pages.lms.staff_view import StaffCoursewarePage
 from common.test.acceptance.pages.studio.import_export import (
     ExportCoursePage,
     ExportLibraryPage,
@@ -18,6 +13,7 @@ from common.test.acceptance.pages.studio.import_export import (
 from common.test.acceptance.pages.studio.library import LibraryEditPage
 from common.test.acceptance.pages.studio.overview import CourseOutlinePage
 from common.test.acceptance.tests.studio.base_studio_test import StudioCourseTest, StudioLibraryTest
+from openedx.core.lib.tests import attr
 
 
 class ExportTestMixin(object):
diff --git a/common/test/acceptance/tests/studio/test_studio_components.py b/common/test/acceptance/tests/studio/test_studio_components.py
index e51b67cc54ed6cdc5e0cd6b6e8a843ceddaec8b3..07a6a9e32732a6b73c6d8df5452d6679372ecb5c 100644
--- a/common/test/acceptance/tests/studio/test_studio_components.py
+++ b/common/test/acceptance/tests/studio/test_studio_components.py
@@ -2,7 +2,6 @@
 Acceptance tests for adding components in Studio.
 """
 import ddt
-from nose.plugins.attrib import attr
 
 from common.test.acceptance.fixtures.course import XBlockFixtureDesc
 from common.test.acceptance.pages.studio.container import ContainerPage
@@ -11,13 +10,14 @@ from common.test.acceptance.pages.studio.utils import add_component, add_compone
 from common.test.acceptance.tests.studio.base_studio_test import ContainerBase
 
 
-@attr(shard=22)
 @ddt.ddt
 class AdvancedProblemComponentTest(ContainerBase):
     """
     Feature: CMS.Component Adding
     As a course author, I want to be able to add a wide variety of components
     """
+    shard = 22
+
     def setUp(self, is_staff=True):
         """
         Create a course with a section, subsection, and unit to which to add the component.
@@ -70,12 +70,13 @@ class AdvancedProblemComponentTest(ContainerBase):
         self.assertEqual(problem.name, component)
 
 
-@attr(shard=22)
 class ComponentTest(ContainerBase):
     """
     Test class to add different components.
     (Not the advanced components)
     """
+    shard = 22
+
     def setUp(self, is_staff=True):
         """
         Create a course with a section, subsection, and unit to which to add the component.
diff --git a/common/test/acceptance/tests/studio/test_studio_container.py b/common/test/acceptance/tests/studio/test_studio_container.py
index 32d5554b7a5a7b47fa7aa7787f6471f181075e5c..958c496f13e09375e8e167c71285fc8c0d8f100a 100644
--- a/common/test/acceptance/tests/studio/test_studio_container.py
+++ b/common/test/acceptance/tests/studio/test_studio_container.py
@@ -6,7 +6,6 @@ for displaying containers within units.
 import datetime
 
 import ddt
-from nose.plugins.attrib import attr
 
 from base_studio_test import ContainerBase
 from common.test.acceptance.fixtures.course import XBlockFixtureDesc
@@ -19,6 +18,7 @@ from common.test.acceptance.pages.studio.html_component_editor import HtmlXBlock
 from common.test.acceptance.pages.studio.move_xblock import MoveModalView
 from common.test.acceptance.pages.studio.utils import add_discussion
 from common.test.acceptance.tests.helpers import create_user_partition_json
+from openedx.core.lib.tests import attr
 from xmodule.partitions.partitions import ENROLLMENT_TRACK_PARTITION_ID, MINIMUM_STATIC_PARTITION_ID, Group
 
 
diff --git a/common/test/acceptance/tests/studio/test_studio_course_create.py b/common/test/acceptance/tests/studio/test_studio_course_create.py
index aece1d8c8a7dc53a950b3d5de4ca8be252a355d5..0a29d871881dd8e20c0bc9672bd38977b7658b70 100644
--- a/common/test/acceptance/tests/studio/test_studio_course_create.py
+++ b/common/test/acceptance/tests/studio/test_studio_course_create.py
@@ -5,19 +5,17 @@ import uuid
 import random
 import string
 
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.studio.index import DashboardPage
 from common.test.acceptance.pages.studio.overview import CourseOutlinePage
 from common.test.acceptance.tests.helpers import AcceptanceTest
 
 
-@attr(shard=19)
 class CreateCourseTest(AcceptanceTest):
     """
     Test that we can create a new course the studio home page.
     """
+    shard = 19
 
     def setUp(self):
         """
diff --git a/common/test/acceptance/tests/studio/test_studio_course_team.py b/common/test/acceptance/tests/studio/test_studio_course_team.py
index 98fcfdc1861440b837884ca38d3d7a7b55ead207..09d02065b06403797a295c352439d0c8f9e411c4 100644
--- a/common/test/acceptance/tests/studio/test_studio_course_team.py
+++ b/common/test/acceptance/tests/studio/test_studio_course_team.py
@@ -1,17 +1,16 @@
 """
 Acceptance tests for course in studio
 """
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.studio.index import DashboardPage
 from common.test.acceptance.pages.studio.users import CourseTeamPage
 from common.test.acceptance.tests.studio.base_studio_test import StudioCourseTest
 
 
-@attr(shard=2)
 class CourseTeamPageTest(StudioCourseTest):
     """ As a course author, I want to be able to add others to my team """
+    shard = 2
+
     def _make_user(self, username):
         """ Registers user and returns user representation dictionary as expected by `log_in` function """
         user = {
diff --git a/common/test/acceptance/tests/studio/test_studio_help.py b/common/test/acceptance/tests/studio/test_studio_help.py
index eee488b4e911a785251abd470c3548c634813bd8..3112e2c1197208acc771d8a7653b0cc39aefe341 100644
--- a/common/test/acceptance/tests/studio/test_studio_help.py
+++ b/common/test/acceptance/tests/studio/test_studio_help.py
@@ -4,8 +4,6 @@ Test the Studio help links.
 
 from unittest import skip
 
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.fixtures.course import XBlockFixtureDesc
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
 from common.test.acceptance.pages.studio.asset_index import AssetIndexPageStudioFrontend
@@ -35,6 +33,7 @@ from common.test.acceptance.tests.helpers import (
     url_for_help
 )
 from common.test.acceptance.tests.studio.base_studio_test import ContainerBase, StudioCourseTest, StudioLibraryTest
+from openedx.core.lib.tests import attr
 
 
 def _get_expected_documentation_url(path):
diff --git a/common/test/acceptance/tests/studio/test_studio_library.py b/common/test/acceptance/tests/studio/test_studio_library.py
index 07d357504d572a7d199b93e46a9275b0e8c8be1c..398a944ccc9eab8fa44fa97b4daf70607ae6e280 100644
--- a/common/test/acceptance/tests/studio/test_studio_library.py
+++ b/common/test/acceptance/tests/studio/test_studio_library.py
@@ -2,7 +2,6 @@
 Acceptance tests for Content Libraries in Studio
 """
 from ddt import data, ddt
-from nose.plugins.attrib import attr
 
 from common.test.acceptance.fixtures.course import XBlockFixtureDesc
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
@@ -10,6 +9,7 @@ from common.test.acceptance.pages.studio.library import LibraryEditPage
 from common.test.acceptance.pages.studio.users import LibraryUsersPage
 from common.test.acceptance.pages.studio.utils import add_component
 from common.test.acceptance.tests.studio.base_studio_test import StudioLibraryTest
+from openedx.core.lib.tests import attr
 
 
 @attr(shard=15)
diff --git a/common/test/acceptance/tests/studio/test_studio_library_container.py b/common/test/acceptance/tests/studio/test_studio_library_container.py
index bced8d7763093eeebdb44f735bf869c5d8f64467..3ad265efd72ec4f2b4c9beea7fee5557b9a9c635 100644
--- a/common/test/acceptance/tests/studio/test_studio_library_container.py
+++ b/common/test/acceptance/tests/studio/test_studio_library_container.py
@@ -4,7 +4,6 @@ Acceptance tests for Library Content in LMS
 import textwrap
 
 import ddt
-from nose.plugins.attrib import attr
 
 from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
 from common.test.acceptance.pages.studio.library import StudioLibraryContainerXBlockWrapper, StudioLibraryContentEditor
@@ -17,12 +16,13 @@ SUBSECTION_NAME = 'Test Subsection'
 UNIT_NAME = 'Test Unit'
 
 
-@attr(shard=17)
 @ddt.ddt
 class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest, TestWithSearchIndexMixin):
     """
     Test Library Content block in LMS
     """
+    shard = 17
+
     def setUp(self):
         """
         Install library with some content and a course using fixtures
diff --git a/common/test/acceptance/tests/studio/test_studio_outline.py b/common/test/acceptance/tests/studio/test_studio_outline.py
index 6a4e27fd86ffbcec1d84675adcc64eafb20ff1ae..cfc839e37b4b56c6274c8f7964ceb6fd4616a161 100644
--- a/common/test/acceptance/tests/studio/test_studio_outline.py
+++ b/common/test/acceptance/tests/studio/test_studio_outline.py
@@ -7,17 +7,14 @@ import json
 from datetime import datetime, timedelta
 from unittest import skip
 
-from nose.plugins.attrib import attr
 from pytz import UTC
 
 from base_studio_test import StudioCourseTest
 from common.test.acceptance.fixtures.config import ConfigModelFixture
 from common.test.acceptance.fixtures.course import XBlockFixtureDesc
-from common.test.acceptance.pages.common.utils import add_enrollment_course_modes
 from common.test.acceptance.pages.lms.course_home import CourseHomePage
 from common.test.acceptance.pages.lms.courseware import CoursewarePage
 from common.test.acceptance.pages.lms.progress import ProgressPage
-from common.test.acceptance.pages.lms.staff_view import StaffCoursewarePage
 from common.test.acceptance.pages.studio.overview import ContainerPage, CourseOutlinePage, ExpandCollapseLinkState
 from common.test.acceptance.pages.studio.settings import SettingsPage
 from common.test.acceptance.pages.studio.checklists import CourseChecklistsPage
@@ -25,6 +22,7 @@ from common.test.acceptance.pages.studio.settings_advanced import AdvancedSettin
 from common.test.acceptance.pages.studio.settings_group_configurations import GroupConfigurationsPage
 from common.test.acceptance.pages.studio.utils import add_discussion, drag, verify_ordering
 from common.test.acceptance.tests.helpers import disable_animations, load_data_str
+from openedx.core.lib.tests import attr
 
 SECTION_NAME = 'Test Section'
 SUBSECTION_NAME = 'Test Subsection'
diff --git a/common/test/acceptance/tests/studio/test_studio_settings.py b/common/test/acceptance/tests/studio/test_studio_settings.py
index 596637e055f7ee75ea7818ef4e87ab1aef925fbe..8d7cf5c89bb790d7c71772f3ac50edca7a8ffd9d 100644
--- a/common/test/acceptance/tests/studio/test_studio_settings.py
+++ b/common/test/acceptance/tests/studio/test_studio_settings.py
@@ -12,7 +12,6 @@ from textwrap import dedent
 
 from bok_choy.promise import EmptyPromise
 from mock import patch
-from nose.plugins.attrib import attr
 
 from common.test.acceptance.tests.studio.base_studio_test import StudioCourseTest
 from common.test.acceptance.fixtures.course import XBlockFixtureDesc
@@ -24,6 +23,7 @@ from common.test.acceptance.pages.studio.settings_advanced import AdvancedSettin
 from common.test.acceptance.pages.studio.settings_group_configurations import GroupConfigurationsPage
 from common.test.acceptance.pages.studio.utils import get_input_value, type_in_codemirror
 from common.test.acceptance.tests.helpers import create_user_partition_json, element_has_text
+from openedx.core.lib.tests import attr
 from xmodule.partitions.partitions import Group
 
 
diff --git a/common/test/acceptance/tests/studio/test_studio_settings_certificates.py b/common/test/acceptance/tests/studio/test_studio_settings_certificates.py
index 1b70d335eafe876979b3caa78e6af9b18bd9c4c9..92366e0b73522558df3ba5adcb879b0e79de8cd1 100644
--- a/common/test/acceptance/tests/studio/test_studio_settings_certificates.py
+++ b/common/test/acceptance/tests/studio/test_studio_settings_certificates.py
@@ -3,8 +3,6 @@ Acceptance tests for Studio's Setting pages
 """
 import re
 
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.pages.lms.create_mode import ModeCreationPage
 from common.test.acceptance.pages.studio.settings_advanced import AdvancedSettingsPage
 from common.test.acceptance.pages.studio.settings_certificates import CertificatesPage
@@ -12,11 +10,12 @@ from common.test.acceptance.tests.helpers import skip_if_browser
 from common.test.acceptance.tests.studio.base_studio_test import StudioCourseTest
 
 
-@attr(shard=22)
 class CertificatesTest(StudioCourseTest):
     """
     Tests for settings/certificates Page.
     """
+    shard = 22
+
     def setUp(self):  # pylint: disable=arguments-differ
         super(CertificatesTest, self).setUp(is_staff=True, test_xss=False)
         self.certificates_page = CertificatesPage(
diff --git a/common/test/acceptance/tests/studio/test_studio_settings_details.py b/common/test/acceptance/tests/studio/test_studio_settings_details.py
index 1564c4a2d6a5e298fa02af1db73418539bbb78fb..e613e96822089906aecd429438db77f42ba0a677 100644
--- a/common/test/acceptance/tests/studio/test_studio_settings_details.py
+++ b/common/test/acceptance/tests/studio/test_studio_settings_details.py
@@ -3,8 +3,6 @@ Acceptance tests for Studio's Settings Details pages
 """
 from datetime import datetime, timedelta
 
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.fixtures.config import ConfigModelFixture
 from common.test.acceptance.fixtures.course import CourseFixture
 from common.test.acceptance.pages.studio.overview import CourseOutlinePage
@@ -18,9 +16,9 @@ from common.test.acceptance.tests.helpers import (
 from common.test.acceptance.tests.studio.base_studio_test import StudioCourseTest
 
 
-@attr(shard=4)
 class StudioSettingsDetailsTest(StudioCourseTest):
     """Base class for settings and details page tests."""
+    shard = 4
 
     def setUp(self, is_staff=True):
         super(StudioSettingsDetailsTest, self).setUp(is_staff=is_staff)
@@ -35,11 +33,12 @@ class StudioSettingsDetailsTest(StudioCourseTest):
         self.settings_detail.visit()
 
 
-@attr(shard=4)
 class SettingsMilestonesTest(StudioSettingsDetailsTest):
     """
     Tests for milestones feature in Studio's settings tab
     """
+    shard = 4
+
     def test_page_has_prerequisite_field(self):
         """
         Test to make sure page has pre-requisite course field if milestones app is enabled.
@@ -168,9 +167,9 @@ class SettingsMilestonesTest(StudioSettingsDetailsTest):
         ))
 
 
-@attr(shard=4)
 class CoursePacingTest(StudioSettingsDetailsTest):
     """Tests for setting a course to self-paced."""
+    shard = 4
 
     def populate_course_fixture(self, __):
         ConfigModelFixture('/config/self_paced', {'enabled': True}).install()
diff --git a/common/test/acceptance/tests/studio/test_studio_split_test.py b/common/test/acceptance/tests/studio/test_studio_split_test.py
index d8d2f2a5675d50f8c3299d9dad19b188f51110d8..2c917a9fcb0b44cf6fb6d774392bd52061d467cf 100644
--- a/common/test/acceptance/tests/studio/test_studio_split_test.py
+++ b/common/test/acceptance/tests/studio/test_studio_split_test.py
@@ -5,7 +5,6 @@ Acceptance tests for Studio related to the split_test module.
 import math
 
 from bok_choy.promise import Promise
-from nose.plugins.attrib import attr
 from selenium.webdriver.support.ui import Select
 
 from base_studio_test import StudioCourseTest
@@ -62,12 +61,12 @@ class SplitTestMixin(object):
         Promise(missing_groups_button_not_present, "Add missing groups button should not be showing.").fulfill()
 
 
-@attr(shard=15)
 class SplitTest(ContainerBase, SplitTestMixin):
     """
     Tests for creating and editing split test instances in Studio.
     """
     __test__ = True
+    shard = 15
 
     def setUp(self):
         super(SplitTest, self).setUp()
@@ -177,11 +176,12 @@ class SplitTest(ContainerBase, SplitTestMixin):
         self.verify_groups(container, ['alpha'], [], verify_missing_groups_not_present=False)
 
 
-@attr(shard=15)
 class GroupConfigurationsNoSplitTest(StudioCourseTest):
     """
     Tests how the Group Configuration page should look when the split_test module is not enabled.
     """
+    shard = 15
+
     def setUp(self):
         super(GroupConfigurationsNoSplitTest, self).setUp()
         self.group_configurations_page = GroupConfigurationsPage(
@@ -202,13 +202,13 @@ class GroupConfigurationsNoSplitTest(StudioCourseTest):
         self.assertFalse(self.group_configurations_page.experiment_group_sections_present)
 
 
-@attr(shard=15)
 class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
     """
     Tests that Group Configurations page works correctly with previously
     added configurations in Studio
     """
     __test__ = True
+    shard = 15
 
     def setUp(self):
         super(GroupConfigurationsTest, self).setUp()
diff --git a/common/test/acceptance/tests/studio/test_studio_textbooks.py b/common/test/acceptance/tests/studio/test_studio_textbooks.py
index cfa93e509a04ee628f02d36b33d00f20e16c818c..7da44aef0d76aa9736cc38054e27dc045ddbfb45 100644
--- a/common/test/acceptance/tests/studio/test_studio_textbooks.py
+++ b/common/test/acceptance/tests/studio/test_studio_textbooks.py
@@ -1,12 +1,11 @@
 """
 Acceptance tests for Studio related to the textbooks.
 """
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.pages.lms.textbook_view import TextbookViewPage
 from common.test.acceptance.pages.studio.textbook_upload import TextbookUploadPage
 from common.test.acceptance.tests.helpers import disable_animations
 from common.test.acceptance.tests.studio.base_studio_test import StudioCourseTest
+from openedx.core.lib.tests import attr
 
 
 class TextbooksTest(StudioCourseTest):
diff --git a/common/test/acceptance/tests/test_cohorted_courseware.py b/common/test/acceptance/tests/test_cohorted_courseware.py
index 1eba04a0c8bf9dbe40d9a328aeb3c6f190aa8a61..34210c01ee06455bebdb0b0f876e7758fe49eab3 100644
--- a/common/test/acceptance/tests/test_cohorted_courseware.py
+++ b/common/test/acceptance/tests/test_cohorted_courseware.py
@@ -3,7 +3,6 @@ End-to-end test for cohorted courseware. This uses both Studio and LMS.
 """
 
 from bok_choy.page_object import XSS_INJECTION
-from nose.plugins.attrib import attr
 
 from common.test.acceptance.fixtures.course import XBlockFixtureDesc
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
@@ -20,11 +19,11 @@ AUDIT_TRACK = "Audit"
 VERIFIED_TRACK = "Verified"
 
 
-@attr(shard=5)
 class EndToEndCohortedCoursewareTest(ContainerBase, CohortTestMixin):
     """
     End-to-end of cohorted courseware.
     """
+    shard = 5
 
     def setUp(self, is_staff=True):
 
diff --git a/common/test/acceptance/tests/video/test_studio_video_editor.py b/common/test/acceptance/tests/video/test_studio_video_editor.py
index 5105ef8e1fc27bf1c968ecba13032b864f37ca5c..f6f09e6da0850e8cc8b33b75bbd3c8f46c4b009a 100644
--- a/common/test/acceptance/tests/video/test_studio_video_editor.py
+++ b/common/test/acceptance/tests/video/test_studio_video_editor.py
@@ -4,17 +4,16 @@
 Acceptance tests for CMS Video Editor.
 """
 import ddt
-from nose.plugins.attrib import attr
 from common.test.acceptance.pages.common.utils import confirm_prompt
 from common.test.acceptance.tests.video.test_studio_video_module import CMSVideoBaseTest
 
 
-@attr(shard=6)
 @ddt.ddt
 class VideoEditorTest(CMSVideoBaseTest):
     """
     CMS Video Editor Test Class
     """
+    shard = 6
 
     def _create_video_component(self, subtitles=False):
         """
diff --git a/common/test/acceptance/tests/video/test_studio_video_module.py b/common/test/acceptance/tests/video/test_studio_video_module.py
index 1c3098ff99932f70a956ed070ac3f0ad135c6fb6..3f4904d759d7cffcc5f661f9d93f92d04126ee71 100644
--- a/common/test/acceptance/tests/video/test_studio_video_module.py
+++ b/common/test/acceptance/tests/video/test_studio_video_module.py
@@ -6,8 +6,8 @@ Acceptance tests for CMS Video Module.
 import os
 from unittest import skipIf
 
+import pytest
 from mock import patch
-from nose.plugins.attrib import attr
 
 from bok_choy.promise import EmptyPromise
 
@@ -171,11 +171,11 @@ class CMSVideoBaseTest(UniqueCourseTest):
         self.unit_page.xblocks[1].save_settings()
 
 
-@attr(shard=13)
 class CMSVideoTest(CMSVideoBaseTest):
     """
     CMS Video Test Class
     """
+    shard = 13
 
     def test_youtube_stub_proxy(self):
         """
@@ -334,7 +334,7 @@ class CMSVideoTest(CMSVideoBaseTest):
         self.video.click_player_button('play')
 
 
-@attr('a11y')
+@pytest.mark.a11y
 class CMSVideoA11yTest(CMSVideoBaseTest):
     """
     CMS Video Accessibility Test Class
diff --git a/common/test/acceptance/tests/video/test_studio_video_transcript.py b/common/test/acceptance/tests/video/test_studio_video_transcript.py
index 94d01b3ed35147cf070b179496fa5cc7a756c34f..1fea22a385ab42fe92a6ff9ff0ddf26c8bdcb8c5 100644
--- a/common/test/acceptance/tests/video/test_studio_video_transcript.py
+++ b/common/test/acceptance/tests/video/test_studio_video_transcript.py
@@ -18,16 +18,14 @@ front-end validation will not pass.
                   one stored on YouTube
     t_not_exist - this file does not exist on YouTube; it exists locally
 """
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.tests.video.test_studio_video_module import CMSVideoBaseTest
 
 
-@attr(shard=18)
 class VideoTranscriptTest(CMSVideoBaseTest):
     """
     CMS Video Transcript Test Class
     """
+    shard = 18
 
     def _create_video_component(self, subtitles=False, subtitle_id='3_yD_cEKoCk'):
         """
diff --git a/common/test/acceptance/tests/video/test_video_events.py b/common/test/acceptance/tests/video/test_video_events.py
index 11169bcef4f3974900860dbb1f2659c26f0dcbbc..0313769eccfa2d7b8c544b5ae1bcf444917b1672 100644
--- a/common/test/acceptance/tests/video/test_video_events.py
+++ b/common/test/acceptance/tests/video/test_video_events.py
@@ -5,7 +5,6 @@ import json
 from unittest import skip
 
 import ddt
-from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey, UsageKey
 
 from common.test.acceptance.pages.lms.video.video import _parse_time_str
@@ -59,9 +58,9 @@ class VideoEventsTestMixin(EventsTestMixin, VideoBaseTest):
         )
 
 
-@attr(shard=21)
 class VideoEventsTest(VideoEventsTestMixin):
     """ Test video player event emission """
+    shard = 21
 
     def test_video_control_events(self):
         """
@@ -190,10 +189,10 @@ class VideoHLSEventsTest(VideoEventsTestMixin):
         self.assert_events_match(expected_events, captured_events)
 
 
-@attr(shard=19)
 @ddt.ddt
 class VideoBumperEventsTest(VideoEventsTestMixin):
     """ Test bumper video event emission """
+    shard = 19
 
     # helper methods
     def watch_video_and_skip(self):
diff --git a/common/test/acceptance/tests/video/test_video_handout.py b/common/test/acceptance/tests/video/test_video_handout.py
index 2c806ac60aec5f8aae66e37154d0a566139f1220..14efed15c036a765c36aed817cf34e10eff1253a 100644
--- a/common/test/acceptance/tests/video/test_video_handout.py
+++ b/common/test/acceptance/tests/video/test_video_handout.py
@@ -3,16 +3,14 @@
 """
 Acceptance tests for CMS Video Handout.
 """
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.tests.video.test_studio_video_module import CMSVideoBaseTest
 
 
-@attr(shard=17)
 class VideoHandoutTest(CMSVideoBaseTest):
     """
     CMS Video Handout Test Class
     """
+    shard = 17
 
     def _create_course_unit_with_handout(self, handout_filename, save_settings=True):
         """
diff --git a/common/test/acceptance/tests/video/test_video_license.py b/common/test/acceptance/tests/video/test_video_license.py
index 0f57b75221953ce58cf193b674232b2fe0cfa6f3..f5ad8b9be3e9c66c95ba82c9c8a18f5aeb7ec158 100644
--- a/common/test/acceptance/tests/video/test_video_license.py
+++ b/common/test/acceptance/tests/video/test_video_license.py
@@ -4,20 +4,19 @@ Acceptance tests for licensing of the Video module
 """
 from __future__ import unicode_literals
 
-from nose.plugins.attrib import attr
-
 from common.test.acceptance.fixtures.course import XBlockFixtureDesc
 from common.test.acceptance.pages.lms.courseware import CoursewarePage
 from common.test.acceptance.pages.studio.overview import CourseOutlinePage
 from common.test.acceptance.tests.studio.base_studio_test import StudioCourseTest
 
 
-@attr(shard=22)
 class VideoLicenseTest(StudioCourseTest):
     """
     Tests for video module-level licensing (that is, setting the license,
     for a specific video module, to All Rights Reserved or Creative Commons)
     """
+    shard = 22
+
     def setUp(self):  # pylint: disable=arguments-differ
         super(VideoLicenseTest, self).setUp()
 
diff --git a/common/test/acceptance/tests/video/test_video_module.py b/common/test/acceptance/tests/video/test_video_module.py
index a9350804f8c7f332e0a87f1671b5c1ddffdd32e4..e04dae2581478023706a73e8d0e9b30aacd35cb1 100644
--- a/common/test/acceptance/tests/video/test_video_module.py
+++ b/common/test/acceptance/tests/video/test_video_module.py
@@ -8,9 +8,6 @@ from unittest import skipIf
 
 from ddt import data, ddt, unpack
 from mock import patch
-from nose.plugins.attrib import attr
-from selenium.webdriver.common.action_chains import ActionChains
-from selenium.webdriver.common.by import By
 
 from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
 from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
@@ -24,6 +21,7 @@ from common.test.acceptance.tests.helpers import (
     is_youtube_available,
     skip_if_browser
 )
+from openedx.core.lib.tests import attr
 
 VIDEO_SOURCE_PORT = 8777
 VIDEO_HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', 'localhost')
diff --git a/scripts/thresholds.sh b/scripts/thresholds.sh
index 3733daa9084c7af5342dcc5d5b6ed2c09a752b36..31f516bdcf2d79de263a3aadf875b1fcb4467d65 100755
--- a/scripts/thresholds.sh
+++ b/scripts/thresholds.sh
@@ -2,6 +2,6 @@
 set -e
 
 export LOWER_PYLINT_THRESHOLD=1000
-export UPPER_PYLINT_THRESHOLD=3825
+export UPPER_PYLINT_THRESHOLD=3800
 export ESLINT_THRESHOLD=5590
 export STYLELINT_THRESHOLD=973