diff --git a/.pylintrc b/.pylintrc
index 49fcf80eb947405af455359bf9ea7158dfeffb35..792079ce0329c3f15f7b80ac592a4bd22d3710fd 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -34,10 +34,13 @@ load-plugins=
 # multiple time (only on the command line, not in the configuration file where
 # it should appear only once).
 disable=
+# Never going to use these
 # C0301: Line too long
-# C0302: Too many lines in module
-# W0141: Used builtin function 'map'
 # W0142: Used * or ** magic
+# W0141: Used builtin function 'map'
+
+# Might use these when the code is in better shape
+# C0302: Too many lines in module
 # R0201: Method could be a function
 # R0901: Too many ancestors
 # R0902: Too many instance attributes
@@ -96,7 +99,18 @@ zope=no
 # List of members which are set dynamically and missed by pylint inference
 # system, and so shouldn't trigger E0201 when accessed. Python regular
 # expressions are accepted.
-generated-members=REQUEST,acl_users,aq_parent,objects,DoesNotExist,can_read,can_write,get_url,size,content
+generated-members=
+    REQUEST,
+    acl_users,
+    aq_parent,
+    objects,
+    DoesNotExist,
+    can_read,
+    can_write,
+    get_url,
+    size,
+    content,
+    status_code
 
 
 [BASIC]
diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py
index c5fb0a231b1a71b785b54a973a8e0dc85fb33dde..e40d7c57b98de7eda854bcbfca181fd77c367f8e 100644
--- a/cms/djangoapps/contentstore/tests/test_contentstore.py
+++ b/cms/djangoapps/contentstore/tests/test_contentstore.py
@@ -15,8 +15,9 @@ from datetime import timedelta
 from django.contrib.auth.models import User
 from django.dispatch import Signal
 from contentstore.utils import get_modulestore
+from contentstore.tests.utils import parse_json
 
-from .utils import ModuleStoreTestCase, parse_json
+from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
 
 from xmodule.modulestore import Location
@@ -48,6 +49,7 @@ class MongoCollectionFindWrapper(object):
         self.counter = self.counter+1
         return self.original(query, *args, **kwargs)
 
+
 @override_settings(MODULESTORE=TEST_DATA_MODULESTORE)
 class ContentStoreToyCourseTest(ModuleStoreTestCase):
     """
@@ -187,27 +189,33 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
     def test_get_depth_with_drafts(self):
         import_from_xml(modulestore(), 'common/test/data/', ['simple'])
 
-        course = modulestore('draft').get_item(Location(['i4x', 'edX', 'simple',
-                                                         'course', '2012_Fall', None]), depth=None)
+        course = modulestore('draft').get_item(
+            Location(['i4x', 'edX', 'simple', 'course', '2012_Fall', None]),
+            depth=None
+        )
 
         # make sure no draft items have been returned
         num_drafts = self._get_draft_counts(course)
         self.assertEqual(num_drafts, 0)
 
-        problem = modulestore('draft').get_item(Location(['i4x', 'edX', 'simple',
-                                                          'problem', 'ps01-simple', None]))
+        problem = modulestore('draft').get_item(
+            Location(['i4x', 'edX', 'simple', 'problem', 'ps01-simple', None])
+        )
 
         # put into draft
         modulestore('draft').clone_item(problem.location, problem.location)
 
         # make sure we can query that item and verify that it is a draft
-        draft_problem = modulestore('draft').get_item(Location(['i4x', 'edX', 'simple',
-                                                                'problem', 'ps01-simple', None]))
+        draft_problem = modulestore('draft').get_item(
+            Location(['i4x', 'edX', 'simple', 'problem', 'ps01-simple', None])
+        )
         self.assertTrue(getattr(draft_problem, 'is_draft', False))
 
         #now requery with depth
-        course = modulestore('draft').get_item(Location(['i4x', 'edX', 'simple',
-                                                         'course', '2012_Fall', None]), depth=None)
+        course = modulestore('draft').get_item(
+            Location(['i4x', 'edX', 'simple', 'course', '2012_Fall', None]),
+            depth=None
+        )
 
         # make sure just one draft item have been returned
         num_drafts = self._get_draft_counts(course)
@@ -266,10 +274,11 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
         # make sure the parent no longer points to the child object which was deleted
         self.assertTrue(sequential.location.url() in chapter.children)
 
-        self.client.post(reverse('delete_item'),
-                         json.dumps({'id': sequential.location.url(), 'delete_children': 'true',
-                                     'delete_all_versions': 'true'}),
-                         "application/json")
+        self.client.post(
+            reverse('delete_item'),
+            json.dumps({'id': sequential.location.url(), 'delete_children': 'true', 'delete_all_versions': 'true'}),
+            "application/json"
+        )
 
         found = False
         try:
@@ -537,15 +546,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
         print 'Exporting to tempdir = {0}'.format(root_dir)
 
         # export out to a tempdir
-        exported = False
-        try:
-            export_to_xml(module_store, content_store, location, root_dir, 'test_export')
-            exported = True
-        except Exception:
-            print 'Exception thrown: {0}'.format(traceback.format_exc())
-            pass
-
-        self.assertTrue(exported)
+        export_to_xml(module_store, content_store, location, root_dir, 'test_export')
 
 
 class ContentStoreTest(ModuleStoreTestCase):
@@ -625,10 +626,12 @@ class ContentStoreTest(ModuleStoreTestCase):
         """Test viewing the index page with no courses"""
         # Create a course so there is something to view
         resp = self.client.get(reverse('index'))
-        self.assertContains(resp,
-                            '<h1 class="title-1">My Courses</h1>',
-                            status_code=200,
-                            html=True)
+        self.assertContains(
+            resp,
+            '<h1 class="title-1">My Courses</h1>',
+            status_code=200,
+            html=True
+        )
 
     def test_course_factory(self):
         """Test that the course factory works correctly."""
@@ -645,10 +648,12 @@ class ContentStoreTest(ModuleStoreTestCase):
         """Test viewing the index page with an existing course"""
         CourseFactory.create(display_name='Robot Super Educational Course')
         resp = self.client.get(reverse('index'))
-        self.assertContains(resp,
-                            '<span class="class-name">Robot Super Educational Course</span>',
-                            status_code=200,
-                            html=True)
+        self.assertContains(
+            resp,
+            '<span class="class-name">Robot Super Educational Course</span>',
+            status_code=200,
+            html=True
+        )
 
     def test_course_overview_view_with_course(self):
         """Test viewing the course overview page with an existing course"""
@@ -661,10 +666,12 @@ class ContentStoreTest(ModuleStoreTestCase):
         }
 
         resp = self.client.get(reverse('course_index', kwargs=data))
-        self.assertContains(resp,
-                            '<article class="courseware-overview" data-course-id="i4x://MITx/999/course/Robot_Super_Course">',
-                            status_code=200,
-                            html=True)
+        self.assertContains(
+            resp,
+            '<article class="courseware-overview" data-course-id="i4x://MITx/999/course/Robot_Super_Course">',
+            status_code=200,
+            html=True
+        )
 
     def test_clone_item(self):
         """Test cloning an item. E.g. creating a new section"""
@@ -680,7 +687,10 @@ class ContentStoreTest(ModuleStoreTestCase):
 
         self.assertEqual(resp.status_code, 200)
         data = parse_json(resp)
-        self.assertRegexpMatches(data['id'], '^i4x:\/\/MITx\/999\/chapter\/([0-9]|[a-f]){32}$')
+        self.assertRegexpMatches(
+            data['id'],
+            r"^i4x://MITx/999/chapter/([0-9]|[a-f]){32}$"
+        )
 
     def test_capa_module(self):
         """Test that a problem treats markdown specially."""
diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py
index c3fa665b2c24f422679b9b44e19e97ae64f09e15..c9f6b2053ec996b4137cd063bf858a79c2cd5a36 100644
--- a/cms/djangoapps/contentstore/tests/test_course_settings.py
+++ b/cms/djangoapps/contentstore/tests/test_course_settings.py
@@ -12,7 +12,7 @@ from models.settings.course_details import (CourseDetails, CourseSettingsEncoder
 from models.settings.course_grading import CourseGradingModel
 from contentstore.utils import get_modulestore
 
-from .utils import ModuleStoreTestCase
+from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 from models.settings.course_metadata import CourseMetadata
@@ -47,12 +47,8 @@ class CourseTestCase(ModuleStoreTestCase):
         self.client = Client()
         self.client.login(username=uname, password=password)
 
-        t = 'i4x://edx/templates/course/Empty'
-        o = 'MITx'
-        n = '999'
-        dn = 'Robot Super Course'
-        self.course_location = Location('i4x', o, n, 'course', 'Robot_Super_Course')
-        CourseFactory.create(template=t, org=o, number=n, display_name=dn)
+        course = CourseFactory.create(template='i4x://edx/templates/course/Empty', org='MITx', number='999', display_name='Robot Super Course')
+        self.course_location = course.location
 
 
 class CourseDetailsTestCase(CourseTestCase):
@@ -86,17 +82,25 @@ class CourseDetailsTestCase(CourseTestCase):
         jsondetails = CourseDetails.fetch(self.course_location)
         jsondetails.syllabus = "<a href='foo'>bar</a>"
         # encode - decode to convert date fields and other data which changes form
-        self.assertEqual(CourseDetails.update_from_json(jsondetails.__dict__).syllabus,
-                         jsondetails.syllabus, "After set syllabus")
+        self.assertEqual(
+            CourseDetails.update_from_json(jsondetails.__dict__).syllabus,
+            jsondetails.syllabus, "After set syllabus"
+        )
         jsondetails.overview = "Overview"
-        self.assertEqual(CourseDetails.update_from_json(jsondetails.__dict__).overview,
-                         jsondetails.overview, "After set overview")
+        self.assertEqual(
+            CourseDetails.update_from_json(jsondetails.__dict__).overview,
+            jsondetails.overview, "After set overview"
+        )
         jsondetails.intro_video = "intro_video"
-        self.assertEqual(CourseDetails.update_from_json(jsondetails.__dict__).intro_video,
-                         jsondetails.intro_video, "After set intro_video")
+        self.assertEqual(
+            CourseDetails.update_from_json(jsondetails.__dict__).intro_video,
+            jsondetails.intro_video, "After set intro_video"
+        )
         jsondetails.effort = "effort"
-        self.assertEqual(CourseDetails.update_from_json(jsondetails.__dict__).effort,
-                         jsondetails.effort, "After set effort")
+        self.assertEqual(
+            CourseDetails.update_from_json(jsondetails.__dict__).effort,
+            jsondetails.effort, "After set effort"
+        )
 
 
 class CourseDetailsViewTest(CourseTestCase):
@@ -150,9 +154,7 @@ class CourseDetailsViewTest(CourseTestCase):
 
     @staticmethod
     def struct_to_datetime(struct_time):
-        return datetime.datetime(struct_time.tm_year, struct_time.tm_mon,
-                                 struct_time.tm_mday, struct_time.tm_hour,
-                                 struct_time.tm_min, struct_time.tm_sec, tzinfo=UTC())
+        return datetime.datetime(*struct_time[:6], tzinfo=UTC())
 
     def compare_date_fields(self, details, encoded, context, field):
         if details[field] is not None:
@@ -271,18 +273,20 @@ class CourseMetadataEditingTest(CourseTestCase):
         self.assertIn('xqa_key', test_model, 'xqa_key field ')
 
     def test_update_from_json(self):
-        test_model = CourseMetadata.update_from_json(self.course_location,
-                                                     {"advertised_start": "start A",
-                                                      "testcenter_info": {"c": "test"},
-                                                      "days_early_for_beta": 2})
+        test_model = CourseMetadata.update_from_json(self.course_location, {
+            "advertised_start": "start A",
+            "testcenter_info": {"c": "test"},
+            "days_early_for_beta": 2
+        })
         self.update_check(test_model)
         # try fresh fetch to ensure persistence
         test_model = CourseMetadata.fetch(self.course_location)
         self.update_check(test_model)
         # now change some of the existing metadata
-        test_model = CourseMetadata.update_from_json(self.course_location,
-                                                     {"advertised_start": "start B",
-                                                      "display_name": "jolly roger"})
+        test_model = CourseMetadata.update_from_json(self.course_location, {
+            "advertised_start": "start B",
+            "display_name": "jolly roger"}
+        )
         self.assertIn('display_name', test_model, 'Missing editable metadata field')
         self.assertEqual(test_model['display_name'], 'jolly roger', "not expected value")
         self.assertIn('advertised_start', test_model, 'Missing revised advertised_start metadata field')
diff --git a/cms/djangoapps/contentstore/tests/test_utils.py b/cms/djangoapps/contentstore/tests/test_utils.py
index 6b1b6b7f6f3101877a2ae971a3e73035790294b0..eb7bfb6db9d4149410e33b318e2bdd11bd6192f1 100644
--- a/cms/djangoapps/contentstore/tests/test_utils.py
+++ b/cms/djangoapps/contentstore/tests/test_utils.py
@@ -3,7 +3,7 @@ from contentstore import utils
 import mock
 from django.test import TestCase
 from xmodule.modulestore.tests.factories import CourseFactory
-from .utils import ModuleStoreTestCase
+from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 
 
 class LMSLinksTestCase(TestCase):
@@ -30,7 +30,7 @@ class LMSLinksTestCase(TestCase):
 
 class UrlReverseTestCase(ModuleStoreTestCase):
     """ Tests for get_url_reverse """
-    def test_CoursePageNames(self):
+    def test_course_page_names(self):
         """ Test the defined course pages. """
         course = CourseFactory.create(org='mitX', number='666', display_name='URL Reverse Course')
 
@@ -70,4 +70,3 @@ class UrlReverseTestCase(ModuleStoreTestCase):
             'https://edge.edx.org/courses/edX/edX101/How_to_Create_an_edX_Course/about',
             utils.get_url_reverse('https://edge.edx.org/courses/edX/edX101/How_to_Create_an_edX_Course/about', course)
         )
-        
\ No newline at end of file
diff --git a/cms/djangoapps/contentstore/tests/tests.py b/cms/djangoapps/contentstore/tests/tests.py
index 72e30dca6437093f4d80348e6694f9291cb14c40..34e5da4b4dc41ca8acb592558943b8c4a40aae74 100644
--- a/cms/djangoapps/contentstore/tests/tests.py
+++ b/cms/djangoapps/contentstore/tests/tests.py
@@ -1,7 +1,8 @@
 from django.test.client import Client
 from django.core.urlresolvers import reverse
 
-from .utils import ModuleStoreTestCase, parse_json, user, registration
+from .utils import parse_json, user, registration
+from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 
 
 class ContentStoreTestCase(ModuleStoreTestCase):
diff --git a/cms/djangoapps/contentstore/tests/utils.py b/cms/djangoapps/contentstore/tests/utils.py
index bb7ac2bf0614e87ad05ec00f822ec4f49815e92f..3135e49a089e43bd42a151c9b61b4e5e65649cb6 100644
--- a/cms/djangoapps/contentstore/tests/utils.py
+++ b/cms/djangoapps/contentstore/tests/utils.py
@@ -2,112 +2,11 @@
 Utilities for contentstore tests
 '''
 
-#pylint: disable=W0603
-
 import json
-import copy
-from uuid import uuid4
-from django.test import TestCase
-from django.conf import settings
 
 from student.models import Registration
 from django.contrib.auth.models import User
 
-import xmodule.modulestore.django
-from xmodule.templates import update_templates
-
-
-class ModuleStoreTestCase(TestCase):
-    """ Subclass for any test case that uses the mongodb
-    module store. This populates a uniquely named modulestore
-    collection with templates before running the TestCase
-    and drops it they are finished. """
-
-    @staticmethod
-    def flush_mongo_except_templates():
-        '''
-        Delete everything in the module store except templates
-        '''
-        modulestore = xmodule.modulestore.django.modulestore()
-
-        # This query means: every item in the collection
-        # that is not a template
-        query = {"_id.course": {"$ne": "templates"}}
-
-        # Remove everything except templates
-        modulestore.collection.remove(query)
-
-    @staticmethod
-    def load_templates_if_necessary():
-        '''
-        Load templates into the modulestore only if they do not already exist.
-        We need the templates, because they are copied to create
-        XModules such as sections and problems
-        '''
-        modulestore = xmodule.modulestore.django.modulestore()
-
-        # Count the number of templates
-        query = {"_id.course": "templates"}
-        num_templates = modulestore.collection.find(query).count()
-
-        if num_templates < 1:
-            update_templates()
-
-    @classmethod
-    def setUpClass(cls):
-        '''
-        Flush the mongo store and set up templates
-        '''
-
-        # Use a uuid to differentiate
-        # the mongo collections on jenkins.
-        cls.orig_modulestore = copy.deepcopy(settings.MODULESTORE)
-        test_modulestore = cls.orig_modulestore
-        test_modulestore['default']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex
-        test_modulestore['direct']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex
-        xmodule.modulestore.django._MODULESTORES = {}
-
-        settings.MODULESTORE = test_modulestore
-
-        TestCase.setUpClass()
-
-    @classmethod
-    def tearDownClass(cls):
-        '''
-        Revert to the old modulestore settings
-        '''
-
-        # Clean up by dropping the collection
-        modulestore = xmodule.modulestore.django.modulestore()
-        modulestore.collection.drop()
-
-        # Restore the original modulestore settings
-        settings.MODULESTORE = cls.orig_modulestore
-
-    def _pre_setup(self):
-        '''
-        Remove everything but the templates before each test
-        '''
-
-        # Flush anything that is not a template
-        ModuleStoreTestCase.flush_mongo_except_templates()
-
-        # Check that we have templates loaded; if not, load them
-        ModuleStoreTestCase.load_templates_if_necessary()
-
-        # Call superclass implementation
-        super(ModuleStoreTestCase, self)._pre_setup()
-
-    def _post_teardown(self):
-        '''
-        Flush everything we created except the templates
-        '''
-        # Flush anything that is not a template
-        ModuleStoreTestCase.flush_mongo_except_templates()
-
-        # Call superclass implementation
-        super(ModuleStoreTestCase, self)._post_teardown()
-
 
 def parse_json(response):
     """Parse response, which is assumed to be json"""
diff --git a/cms/envs/test.py b/cms/envs/test.py
index 99869bc8697b607808b2c6d082cefcd8d57829fc..f11ff9b56c1bb43c0af1323535039525da72ac3f 100644
--- a/cms/envs/test.py
+++ b/cms/envs/test.py
@@ -28,7 +28,7 @@ GITHUB_REPO_ROOT = TEST_ROOT / "data"
 COMMON_TEST_DATA_ROOT = COMMON_ROOT / "test" / "data"
 
 # Makes the tests run much faster...
-SOUTH_TESTS_MIGRATE = False # To disable migrations and use syncdb instead
+SOUTH_TESTS_MIGRATE = False  # To disable migrations and use syncdb instead
 
 # TODO (cpennington): We need to figure out how envs/test.py can inject things into common.py so that we don't have to repeat this sort of thing
 STATICFILES_DIRS = [
@@ -41,7 +41,7 @@ STATICFILES_DIRS += [
     if os.path.isdir(COMMON_TEST_DATA_ROOT / course_dir)
 ]
 
-modulestore_options = {
+MODULESTORE_OPTIONS = {
     'default_class': 'xmodule.raw_module.RawDescriptor',
     'host': 'localhost',
     'db': 'test_xmodule',
@@ -53,15 +53,15 @@ modulestore_options = {
 MODULESTORE = {
     'default': {
         'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
-        'OPTIONS': modulestore_options
+        'OPTIONS': MODULESTORE_OPTIONS
     },
     'direct': {
         'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
-        'OPTIONS': modulestore_options
+        'OPTIONS': MODULESTORE_OPTIONS
     },
     'draft': {
         'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
-        'OPTIONS': modulestore_options
+        'OPTIONS': MODULESTORE_OPTIONS
     }
 }
 
@@ -76,7 +76,7 @@ CONTENTSTORE = {
 DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': ENV_ROOT / "db" / "cms.db",
+        'NAME': TEST_ROOT / "db" / "cms.db",
     },
 }
 
diff --git a/common/djangoapps/student/tests/factories.py b/common/djangoapps/student/tests/factories.py
index f74188725aadd79c41967ded3c89e0919ea4e86d..adb51954e8ccacd69c41f5e2f7ccaac8bfd9099a 100644
--- a/common/djangoapps/student/tests/factories.py
+++ b/common/djangoapps/student/tests/factories.py
@@ -2,7 +2,7 @@ from student.models import (User, UserProfile, Registration,
                             CourseEnrollmentAllowed, CourseEnrollment)
 from django.contrib.auth.models import Group
 from datetime import datetime
-from factory import Factory, SubFactory
+from factory import Factory, SubFactory, post_generation
 from uuid import uuid4
 
 
@@ -44,6 +44,17 @@ class UserFactory(Factory):
     last_login = datetime(2012, 1, 1)
     date_joined = datetime(2011, 1, 1)
 
+    @post_generation
+    def set_password(self, create, extracted, **kwargs):
+        self._raw_password = self.password
+        self.set_password(self.password)
+        if create:
+            self.save()
+
+
+class AdminFactory(UserFactory):
+    is_staff = True
+
 
 class CourseEnrollmentFactory(Factory):
     FACTORY_FOR = CourseEnrollment
diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py
index 5d3f5761197307ab51920375a032b48a7d58a5de..1ea51bd7f1f0eb56b074696803b0003d9c0cfb4a 100644
--- a/common/lib/xmodule/xmodule/course_module.py
+++ b/common/lib/xmodule/xmodule/course_module.py
@@ -232,6 +232,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
         #   disable the syllabus content for courses that do not provide a syllabus
         self.syllabus_present = self.system.resources_fs.exists(path('syllabus'))
         self._grading_policy = {}
+
         self.set_grading_policy(self.grading_policy)
 
         self.test_center_exams = []
diff --git a/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py b/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..753cbfac4f34acf8546909777ac843df7fb9673b
--- /dev/null
+++ b/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py
@@ -0,0 +1,104 @@
+
+import copy
+from uuid import uuid4
+from django.test import TestCase
+
+from django.conf import settings
+import xmodule.modulestore.django
+from xmodule.templates import update_templates
+
+
+class ModuleStoreTestCase(TestCase):
+    """ Subclass for any test case that uses the mongodb
+    module store. This populates a uniquely named modulestore
+    collection with templates before running the TestCase
+    and drops it they are finished. """
+
+    @staticmethod
+    def flush_mongo_except_templates():
+        '''
+        Delete everything in the module store except templates
+        '''
+        modulestore = xmodule.modulestore.django.modulestore()
+
+        # This query means: every item in the collection
+        # that is not a template
+        query = {"_id.course": {"$ne": "templates"}}
+
+        # Remove everything except templates
+        modulestore.collection.remove(query)
+
+    @staticmethod
+    def load_templates_if_necessary():
+        '''
+        Load templates into the modulestore only if they do not already exist.
+        We need the templates, because they are copied to create
+        XModules such as sections and problems
+        '''
+        modulestore = xmodule.modulestore.django.modulestore()
+
+        # Count the number of templates
+        query = {"_id.course": "templates"}
+        num_templates = modulestore.collection.find(query).count()
+
+        if num_templates < 1:
+            update_templates()
+
+    @classmethod
+    def setUpClass(cls):
+        '''
+        Flush the mongo store and set up templates
+        '''
+
+        # Use a uuid to differentiate
+        # the mongo collections on jenkins.
+        cls.orig_modulestore = copy.deepcopy(settings.MODULESTORE)
+        if 'direct' not in settings.MODULESTORE:
+            settings.MODULESTORE['direct'] = settings.MODULESTORE['default']
+
+        settings.MODULESTORE['default']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex
+        settings.MODULESTORE['direct']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex
+        xmodule.modulestore.django._MODULESTORES.clear()
+
+        print settings.MODULESTORE
+
+        TestCase.setUpClass()
+
+    @classmethod
+    def tearDownClass(cls):
+        '''
+        Revert to the old modulestore settings
+        '''
+
+        # Clean up by dropping the collection
+        modulestore = xmodule.modulestore.django.modulestore()
+        modulestore.collection.drop()
+
+        xmodule.modulestore.django._MODULESTORES.clear()
+
+        # Restore the original modulestore settings
+        settings.MODULESTORE = cls.orig_modulestore
+
+    def _pre_setup(self):
+        '''
+        Remove everything but the templates before each test
+        '''
+
+        # Flush anything that is not a template
+        ModuleStoreTestCase.flush_mongo_except_templates()
+
+        # Check that we have templates loaded; if not, load them
+        ModuleStoreTestCase.load_templates_if_necessary()
+
+        # Call superclass implementation
+        super(ModuleStoreTestCase, self)._pre_setup()
+
+    def _post_teardown(self):
+        '''
+        Flush everything we created except the templates
+        '''
+        # Flush anything that is not a template
+        ModuleStoreTestCase.flush_mongo_except_templates()
+
+        # Call superclass implementation
+        super(ModuleStoreTestCase, self)._post_teardown()
diff --git a/common/lib/xmodule/xmodule/modulestore/tests/factories.py b/common/lib/xmodule/xmodule/modulestore/tests/factories.py
index 1a82e1b7082d76e3ef9c20d2b50bb49ff013ce69..e49972a305dd5a2252215596ce8d6ea08ca9aeba 100644
--- a/common/lib/xmodule/xmodule/modulestore/tests/factories.py
+++ b/common/lib/xmodule/xmodule/modulestore/tests/factories.py
@@ -1,4 +1,4 @@
-from factory import Factory
+from factory import Factory, lazy_attribute_sequence, lazy_attribute
 from time import gmtime
 from uuid import uuid4
 from xmodule.modulestore import Location
@@ -7,21 +7,12 @@ from xmodule.timeparse import stringify_time
 from xmodule.modulestore.inheritance import own_metadata
 
 
-def XMODULE_COURSE_CREATION(class_to_create, **kwargs):
-    return XModuleCourseFactory._create(class_to_create, **kwargs)
-
-
-def XMODULE_ITEM_CREATION(class_to_create, **kwargs):
-    return XModuleItemFactory._create(class_to_create, **kwargs)
-
-
 class XModuleCourseFactory(Factory):
     """
     Factory for XModule courses.
     """
 
     ABSTRACT_FACTORY = True
-    _creation_function = (XMODULE_COURSE_CREATION,)
 
     @classmethod
     def _create(cls, target_class, *args, **kwargs):
@@ -33,7 +24,10 @@ class XModuleCourseFactory(Factory):
         location = Location('i4x', org, number,
                             'course', Location.clean(display_name))
 
-        store = modulestore('direct')
+        try:
+            store = modulestore('direct')
+        except KeyError:
+            store = modulestore()
 
         # Write the data to the mongo datastore
         new_course = store.clone_item(template, location)
@@ -52,6 +46,11 @@ class XModuleCourseFactory(Factory):
         # Update the data in the mongo datastore
         store.update_metadata(new_course.location.url(), own_metadata(new_course))
 
+        data = kwargs.get('data')
+        if data is not None:
+            store.update_item(new_course.location, data)
+
+
         return new_course
 
 
@@ -74,7 +73,19 @@ class XModuleItemFactory(Factory):
     """
 
     ABSTRACT_FACTORY = True
-    _creation_function = (XMODULE_ITEM_CREATION,)
+
+    display_name = None
+
+    @lazy_attribute
+    def category(attr):
+        template = Location(attr.template)
+        return template.category
+
+    @lazy_attribute
+    def location(attr):
+        parent = Location(attr.parent_location)
+        dest_name = attr.display_name.replace(" ", "_") if attr.display_name is not None else uuid4().hex
+        return parent._replace(category=attr.category, name=dest_name)
 
     @classmethod
     def _create(cls, target_class, *args, **kwargs):
@@ -110,12 +121,7 @@ class XModuleItemFactory(Factory):
         # This code was based off that in cms/djangoapps/contentstore/views.py
         parent = store.get_item(parent_location)
 
-        # If a display name is set, use that
-        dest_name = display_name.replace(" ", "_") if display_name is not None else uuid4().hex
-        dest_location = parent_location._replace(category=template.category,
-                                                 name=dest_name)
-
-        new_item = store.clone_item(template, dest_location)
+        new_item = store.clone_item(template, kwargs.get('location'))
 
         # replace the display name with an optional parameter passed in from the caller
         if display_name is not None:
@@ -145,4 +151,7 @@ class ItemFactory(XModuleItemFactory):
 
     parent_location = 'i4x://MITx/999/course/Robot_Super_Course'
     template = 'i4x://edx/templates/chapter/Empty'
-    display_name = 'Section One'
+
+    @lazy_attribute_sequence
+    def display_name(attr, n):
+        return "{} {}".format(attr.category.title(), n)
\ No newline at end of file
diff --git a/jenkins/test.sh b/jenkins/test.sh
index edcca840c84fa9fa5e5a00c7ad06a1a356944b14..b554e7a708c20009adc0e43bd8a5a0ba4c30ce59 100755
--- a/jenkins/test.sh
+++ b/jenkins/test.sh
@@ -36,7 +36,7 @@ export PIP_DOWNLOAD_CACHE=/mnt/pip-cache
 
 source /mnt/virtualenvs/"$JOB_NAME"/bin/activate
 pip install -q -r pre-requirements.txt
-yes w | pip install -q -r test-requirements.txt -r requirements.txt
+yes w | pip install -q -r requirements.txt
 
 rake clobber
 rake pep8 > pep8.log || cat pep8.log
diff --git a/lms/djangoapps/courseware/features/common.py b/lms/djangoapps/courseware/features/common.py
index f6256adfa17326d54e6b3c3a1c13a9861cd8735c..e81568ae4b354b11e15c314287c1fc29693ded5e 100644
--- a/lms/djangoapps/courseware/features/common.py
+++ b/lms/djangoapps/courseware/features/common.py
@@ -1,6 +1,8 @@
 #pylint: disable=C0111
 #pylint: disable=W0621
 
+from __future__ import absolute_import
+
 from lettuce import world, step
 from nose.tools import assert_equals, assert_in
 from lettuce.django import django_url
diff --git a/lms/djangoapps/courseware/tests/factories.py b/lms/djangoapps/courseware/tests/factories.py
index a84b2b8475f25a9c0db68be41fb91e77fecd41ca..df072c015c7f72aee52f952a39e1dfa243bfce26 100644
--- a/lms/djangoapps/courseware/tests/factories.py
+++ b/lms/djangoapps/courseware/tests/factories.py
@@ -1,6 +1,7 @@
 import factory
 from student.models import (User, UserProfile, Registration,
                             CourseEnrollmentAllowed)
+from courseware.models import StudentModule
 from django.contrib.auth.models import Group
 from datetime import datetime
 import uuid
@@ -47,3 +48,15 @@ class CourseEnrollmentAllowedFactory(factory.Factory):
 
     email = 'test@edx.org'
     course_id = 'edX/test/2012_Fall'
+
+
+class StudentModuleFactory(factory.Factory):
+    FACTORY_FOR = StudentModule
+
+    module_type = "problem"
+    student = factory.SubFactory(UserFactory)
+    course_id = "MITx/999/Robot_Super_Course"
+    state = None
+    grade = None
+    max_grade = None
+    done = 'na'
diff --git a/lms/djangoapps/courseware/tests/tests.py b/lms/djangoapps/courseware/tests/tests.py
index 5613f8831fec21f3f7a034e661f2f1d503047665..4c9f592797c2395bb1aba0f3f840ccaec390e110 100644
--- a/lms/djangoapps/courseware/tests/tests.py
+++ b/lms/djangoapps/courseware/tests/tests.py
@@ -55,7 +55,7 @@ def mongo_store_config(data_dir):
 
     Use of this config requires mongo to be running
     '''
-    return {
+    store = {
         'default': {
             'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
             'OPTIONS': {
@@ -68,6 +68,8 @@ def mongo_store_config(data_dir):
             }
         }
     }
+    store['direct'] = store['default']
+    return store
 
 
 def draft_mongo_store_config(data_dir):
@@ -83,6 +85,17 @@ def draft_mongo_store_config(data_dir):
                 'fs_root': data_dir,
                 'render_template': 'mitxmako.shortcuts.render_to_string',
             }
+        },
+        'direct': {
+            'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
+            'OPTIONS': {
+                'default_class': 'xmodule.raw_module.RawDescriptor',
+                'host': 'localhost',
+                'db': 'test_xmodule',
+                'collection': 'modulestore',
+                'fs_root': data_dir,
+                'render_template': 'mitxmako.shortcuts.render_to_string',
+            }
         }
     }
 
diff --git a/lms/djangoapps/django_comment_client/models.py b/lms/djangoapps/django_comment_client/models.py
index 023b355a29ebb58e0829b1e917342db561612318..e06aed1281e711f16b685762b4e8d6ed7177ff2d 100644
--- a/lms/djangoapps/django_comment_client/models.py
+++ b/lms/djangoapps/django_comment_client/models.py
@@ -38,7 +38,7 @@ class Role(models.Model):
     def inherit_permissions(self, role):   # TODO the name of this method is a little bit confusing,
                                          # since it's one-off and doesn't handle inheritance later
         if role.course_id and role.course_id != self.course_id:
-            logging.warning("{0} cannot inherit permissions from {1} due to course_id inconsistency", \
+            logging.warning("%s cannot inherit permissions from %s due to course_id inconsistency", \
                             self, role)
         for per in role.permissions.all():
             self.add_permission(per)
diff --git a/lms/djangoapps/instructor/tests/__init__.py b/lms/djangoapps/instructor/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lms/djangoapps/instructor/tests/test_download_csv.py b/lms/djangoapps/instructor/tests/test_download_csv.py
new file mode 100644
index 0000000000000000000000000000000000000000..8e4c175faa0894338951cab861523039a780ecff
--- /dev/null
+++ b/lms/djangoapps/instructor/tests/test_download_csv.py
@@ -0,0 +1,81 @@
+"""
+Unit tests for instructor dashboard
+
+Based on (and depends on) unit tests for courseware.
+
+Notes for running by hand:
+
+django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/instructor
+"""
+
+from django.test.utils import override_settings
+
+# Need access to internal func to put users in the right group
+from django.contrib.auth.models import Group
+
+from django.core.urlresolvers import reverse
+
+from courseware.access import _course_staff_group_name
+from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE, get_user
+from xmodule.modulestore.django import modulestore
+import xmodule.modulestore.django
+
+
+@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
+class TestInstructorDashboardGradeDownloadCSV(LoginEnrollmentTestCase):
+    '''
+    Check for download of csv
+    '''
+
+    def setUp(self):
+        xmodule.modulestore.django._MODULESTORES = {}
+
+        self.full = modulestore().get_course("edX/full/6.002_Spring_2012")
+        self.toy = modulestore().get_course("edX/toy/2012_Fall")
+
+        # Create two accounts
+        self.student = 'view@test.com'
+        self.instructor = 'view2@test.com'
+        self.password = 'foo'
+        self.create_account('u1', self.student, self.password)
+        self.create_account('u2', self.instructor, self.password)
+        self.activate_user(self.student)
+        self.activate_user(self.instructor)
+
+        def make_instructor(course):
+            group_name = _course_staff_group_name(course.location)
+            g = Group.objects.create(name=group_name)
+            g.user_set.add(get_user(self.instructor))
+
+        make_instructor(self.toy)
+
+        self.logout()
+        self.login(self.instructor, self.password)
+        self.enroll(self.toy)
+
+    def test_download_grades_csv(self):
+        course = self.toy
+        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
+        msg = "url = {0}\n".format(url)
+        response = self.client.post(url, {'action': 'Download CSV of all student grades for this course'})
+        msg += "instructor dashboard download csv grades: response = '{0}'\n".format(response)
+
+        self.assertEqual(response['Content-Type'], 'text/csv', msg)
+
+        cdisp = response['Content-Disposition']
+        msg += "Content-Disposition = '%s'\n" % cdisp
+        self.assertEqual(cdisp, 'attachment; filename=grades_{0}.csv'.format(course.id), msg)
+
+        body = response.content.replace('\r', '')
+        msg += "body = '{0}'\n".format(body)
+
+        # All the not-actually-in-the-course hw and labs come from the
+        # default grading policy string in graders.py
+        expected_body = '''"ID","Username","Full Name","edX email","External email","HW 01","HW 02","HW 03","HW 04","HW 05","HW 06","HW 07","HW 08","HW 09","HW 10","HW 11","HW 12","HW Avg","Lab 01","Lab 02","Lab 03","Lab 04","Lab 05","Lab 06","Lab 07","Lab 08","Lab 09","Lab 10","Lab 11","Lab 12","Lab Avg","Midterm","Final"
+"2","u2","Fred Weasley","view2@test.com","","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"
+'''
+
+        self.assertEqual(body, expected_body, msg)
+
+
+
diff --git a/lms/djangoapps/instructor/tests.py b/lms/djangoapps/instructor/tests/test_forum_admin.py
similarity index 73%
rename from lms/djangoapps/instructor/tests.py
rename to lms/djangoapps/instructor/tests/test_forum_admin.py
index fd8e6529976d7fde6c1ce8e0b574cdc234a9957e..d2d58fb61c97d6d4dd3e936b7de086b59da2df78 100644
--- a/lms/djangoapps/instructor/tests.py
+++ b/lms/djangoapps/instructor/tests/test_forum_admin.py
@@ -1,13 +1,8 @@
 """
-Unit tests for instructor dashboard
-
-Based on (and depends on) unit tests for courseware.
-
-Notes for running by hand:
-
-django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/instructor
+Unit tests for instructor dashboard forum administration
 """
 
+
 from django.test.utils import override_settings
 
 # Need access to internal func to put users in the right group
@@ -24,63 +19,6 @@ from xmodule.modulestore.django import modulestore
 import xmodule.modulestore.django
 
 
-@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
-class TestInstructorDashboardGradeDownloadCSV(LoginEnrollmentTestCase):
-    '''
-    Check for download of csv
-    '''
-
-    def setUp(self):
-        xmodule.modulestore.django._MODULESTORES = {}
-
-        self.full = modulestore().get_course("edX/full/6.002_Spring_2012")
-        self.toy = modulestore().get_course("edX/toy/2012_Fall")
-
-        # Create two accounts
-        self.student = 'view@test.com'
-        self.instructor = 'view2@test.com'
-        self.password = 'foo'
-        self.create_account('u1', self.student, self.password)
-        self.create_account('u2', self.instructor, self.password)
-        self.activate_user(self.student)
-        self.activate_user(self.instructor)
-
-        def make_instructor(course):
-            group_name = _course_staff_group_name(course.location)
-            g = Group.objects.create(name=group_name)
-            g.user_set.add(get_user(self.instructor))
-
-        make_instructor(self.toy)
-
-        self.logout()
-        self.login(self.instructor, self.password)
-        self.enroll(self.toy)
-
-    def test_download_grades_csv(self):
-        course = self.toy
-        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
-        msg = "url = {0}\n".format(url)
-        response = self.client.post(url, {'action': 'Download CSV of all student grades for this course'})
-        msg += "instructor dashboard download csv grades: response = '{0}'\n".format(response)
-
-        self.assertEqual(response['Content-Type'], 'text/csv', msg)
-
-        cdisp = response['Content-Disposition']
-        msg += "Content-Disposition = '%s'\n" % cdisp
-        self.assertEqual(cdisp, 'attachment; filename=grades_{0}.csv'.format(course.id), msg)
-
-        body = response.content.replace('\r', '')
-        msg += "body = '{0}'\n".format(body)
-
-        # All the not-actually-in-the-course hw and labs come from the
-        # default grading policy string in graders.py
-        expected_body = '''"ID","Username","Full Name","edX email","External email","HW 01","HW 02","HW 03","HW 04","HW 05","HW 06","HW 07","HW 08","HW 09","HW 10","HW 11","HW 12","HW Avg","Lab 01","Lab 02","Lab 03","Lab 04","Lab 05","Lab 06","Lab 07","Lab 08","Lab 09","Lab 10","Lab 11","Lab 12","Lab Avg","Midterm","Final"
-"2","u2","Fred Weasley","view2@test.com","","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"
-'''
-
-        self.assertEqual(body, expected_body, msg)
-
-
 FORUM_ROLES = [FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA]
 FORUM_ADMIN_ACTION_SUFFIX = {FORUM_ROLE_ADMINISTRATOR: 'admin', FORUM_ROLE_MODERATOR: 'moderator', FORUM_ROLE_COMMUNITY_TA: 'community TA'}
 FORUM_ADMIN_USER = {FORUM_ROLE_ADMINISTRATOR: 'forumadmin', FORUM_ROLE_MODERATOR: 'forummoderator', FORUM_ROLE_COMMUNITY_TA: 'forummoderator'}
@@ -208,4 +146,4 @@ class TestInstructorDashboardForumAdmin(LoginEnrollmentTestCase):
             added_roles.append(rolename)
             added_roles.sort()
             roles = ', '.join(added_roles)
-            self.assertTrue(response.content.find('<td>{0}</td>'.format(roles)) >= 0, 'not finding roles "{0}"'.format(roles))
+            self.assertTrue(response.content.find('<td>{0}</td>'.format(roles)) >= 0, 'not finding roles "{0}"'.format(roles))
\ No newline at end of file
diff --git a/lms/djangoapps/instructor/tests/test_gradebook.py b/lms/djangoapps/instructor/tests/test_gradebook.py
new file mode 100644
index 0000000000000000000000000000000000000000..2de5c18bcd298a9913cd70bba43c5468f7ca4d82
--- /dev/null
+++ b/lms/djangoapps/instructor/tests/test_gradebook.py
@@ -0,0 +1,154 @@
+"""
+Tests of the instructor dashboard gradebook
+"""
+
+from django.test import TestCase
+from django.test.utils import override_settings
+from django.core.urlresolvers import reverse
+from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
+from student.tests.factories import UserFactory, CourseEnrollmentFactory, UserProfileFactory, AdminFactory
+from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
+from mock import patch, DEFAULT
+from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
+from capa.tests.response_xml_factory import StringResponseXMLFactory
+from courseware.tests.factories import StudentModuleFactory
+from xmodule.modulestore import Location
+from xmodule.modulestore.django import modulestore
+
+
+USER_COUNT = 11
+
+@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
+class TestGradebook(ModuleStoreTestCase):
+    grading_policy = None
+
+    def setUp(self):
+        instructor = AdminFactory.create()
+        self.client.login(username=instructor.username, password='test')
+
+        modulestore().request_cache = modulestore().metadata_inheritance_cache_subsystem = None
+
+        course_data = {}
+        if self.grading_policy is not None:
+            course_data['grading_policy'] = self.grading_policy
+
+        self.course = CourseFactory.create(data=course_data)
+        chapter = ItemFactory.create(
+            parent_location=self.course.location,
+            template="i4x://edx/templates/sequential/Empty",
+        )
+        section = ItemFactory.create(
+            parent_location=chapter.location,
+            template="i4x://edx/templates/sequential/Empty",
+            metadata={'graded': True, 'format': 'Homework'}
+        )
+
+        self.users = [
+            UserFactory.create(username='robot%d' % i, email='robot+test+%d@edx.org' % i)
+            for i in xrange(USER_COUNT)
+        ]
+
+        for user in self.users:
+            UserProfileFactory.create(user=user)
+            CourseEnrollmentFactory.create(user=user, course_id=self.course.id)
+
+        for i in xrange(USER_COUNT-1):
+            template_name = "i4x://edx/templates/problem/Blank_Common_Problem"
+            item = ItemFactory.create(
+                parent_location=section.location,
+                template=template_name,
+                data=StringResponseXMLFactory().build_xml(answer='foo'),
+                metadata={'rerandomize': 'always'}
+            )
+
+            for j, user in enumerate(self.users):
+                StudentModuleFactory.create(
+                    grade=1 if i < j else 0,
+                    max_grade=1,
+                    student=user,
+                    course_id=self.course.id,
+                    module_state_key=Location(item.location).url()
+                )
+
+        self.response = self.client.get(reverse('gradebook', args=(self.course.id,)))
+
+    def test_response_code(self):
+        self.assertEquals(self.response.status_code, 200)
+
+class TestDefaultGradingPolicy(TestGradebook):
+    def test_all_users_listed(self):
+        for user in self.users:
+            self.assertIn(user.username, self.response.content)
+
+    def test_default_policy(self):
+        # Default >= 50% passes, so Users 5-10 should be passing for Homework 1 [6]
+        # One use at the top of the page [1]
+        self.assertEquals(7, self.response.content.count('grade_Pass'))
+
+        # Users 1-5 attempted Homework 1 (and get Fs) [4]
+        # Users 1-10 attempted any homework (and get Fs) [10]
+        # Users 4-10 scored enough to not get rounded to 0 for the class (and get Fs) [7]
+        # One use at top of the page [1]
+        self.assertEquals(22, self.response.content.count('grade_F'))
+
+        # All other grades are None [29 categories * 11 users - 27 non-empty grades = 292]
+        # One use at the top of the page [1]
+        self.assertEquals(293, self.response.content.count('grade_None'))
+
+class TestLetterCutoffPolicy(TestGradebook):
+    grading_policy = {
+        "GRADER": [
+            {
+                "type": "Homework",
+                "min_count": 1,
+                "drop_count": 0,
+                "short_label": "HW",
+                "weight": 1
+            },
+        ],
+        "GRADE_CUTOFFS": {
+            'A': .9,
+            'B': .8,
+            'C': .7,
+            'D': .6,
+        }
+    }
+
+    def test_styles(self):
+
+        self.assertIn("grade_A {color:green;}", self.response.content)
+        self.assertIn("grade_B {color:Chocolate;}", self.response.content)
+        self.assertIn("grade_C {color:DarkSlateGray;}", self.response.content)
+        self.assertIn("grade_D {color:DarkSlateGray;}", self.response.content)
+
+    def test_assigned_grades(self):
+        print self.response.content
+        # Users 9-10 have >= 90% on Homeworks [2]
+        # Users 9-10 have >= 90% on the class [2]
+        # One use at the top of the page [1]
+        self.assertEquals(5, self.response.content.count('grade_A'))
+
+        # User 8 has 80 <= Homeworks < 90 [1]
+        # User 8 has 80 <= class < 90 [1]
+        # One use at the top of the page [1]
+        self.assertEquals(3, self.response.content.count('grade_B'))
+
+        # User 7 has 70 <= Homeworks < 80 [1]
+        # User 7 has 70 <= class < 80 [1]
+        # One use at the top of the page [1]
+        self.assertEquals(3, self.response.content.count('grade_C'))
+
+        # User 6 has 60 <= Homeworks < 70 [1]
+        # User 6 has 60 <= class < 70 [1]
+        # One use at the top of the page [1]
+        self.assertEquals(3, self.response.content.count('grade_C'))
+
+        # Users 1-5 have 60% > grades > 0 on Homeworks [5]
+        # Users 1-5 have 60% > grades > 0 on the class [5]
+        # One use at top of the page [1]
+        self.assertEquals(11, self.response.content.count('grade_F'))
+
+        # User 0 has 0 on Homeworks [1]
+        # User 0 has 0 on the class [1]
+        # One use at the top of the page [1]
+        self.assertEquals(3, self.response.content.count('grade_None'))
\ No newline at end of file
diff --git a/lms/djangoapps/instructor/views.py b/lms/djangoapps/instructor/views.py
index a3b4f42bf768c23e40e818cf4243b07676459705..dd6748e691e96e304bee5b5560d23ff5edeca5a5 100644
--- a/lms/djangoapps/instructor/views.py
+++ b/lms/djangoapps/instructor/views.py
@@ -961,11 +961,14 @@ def gradebook(request, course_id):
                      }
                      for student in enrolled_students]
 
-    return render_to_response('courseware/gradebook.html', {'students': student_info,
-                                                 'course': course,
-                                                 'course_id': course_id,
-                                                 # Checked above
-                                                 'staff_access': True, })
+    return render_to_response('courseware/gradebook.html', {
+        'students': student_info,
+        'course': course,
+        'course_id': course_id,
+        # Checked above
+        'staff_access': True,
+        'ordered_grades': sorted(course.grade_cutoffs.items(), key=lambda i: i[1], reverse=True),
+    })
 
 
 @cache_control(no_cache=True, no_store=True, must_revalidate=True)
diff --git a/lms/djangoapps/licenses/models.py b/lms/djangoapps/licenses/models.py
index 06f777f611067ce1e5ef5b6210d06e2cb96affa8..db24126a8e25b41570a605d5f439f16de5444f6f 100644
--- a/lms/djangoapps/licenses/models.py
+++ b/lms/djangoapps/licenses/models.py
@@ -73,7 +73,7 @@ def _create_license(user, software):
             license.save()
     except IndexError:
         # there are no free licenses
-        log.error('No serial numbers available for {0}', software)
+        log.error('No serial numbers available for %s', software)
         license = None
         # TODO [rocha]look if someone has unenrolled from the class
         # and already has a serial number
diff --git a/lms/djangoapps/licenses/tests.py b/lms/djangoapps/licenses/tests.py
index 5289c31bc6663cfeea1440d380188c1b7d2fb545..5cf5e44dde8ad4907ea20c7469bd06e9d01515c0 100644
--- a/lms/djangoapps/licenses/tests.py
+++ b/lms/djangoapps/licenses/tests.py
@@ -8,10 +8,14 @@ from tempfile import NamedTemporaryFile
 from factory import Factory, SubFactory
 
 from django.test import TestCase
+from django.test.utils import override_settings
 from django.core.management import call_command
 from django.core.urlresolvers import reverse
+from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
 from licenses.models import CourseSoftware, UserLicense
 from courseware.tests.tests import LoginEnrollmentTestCase, get_user
+from xmodule.modulestore.tests.factories import CourseFactory
+from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 
 COURSE_1 = 'edX/toy/2012_Fall'
 
@@ -130,20 +134,24 @@ class LicenseTestCase(LoginEnrollmentTestCase):
         self.assertEqual(302, response.status_code)
 
 
-class CommandTest(TestCase):
+@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
+class CommandTest(ModuleStoreTestCase):
     '''Test management command for importing serial numbers'''
+    def setUp(self):
+        course = CourseFactory.create()
+        self.course_id = course.id
 
     def test_import_serial_numbers(self):
         size = 20
 
         log.debug('Adding one set of serials for {0}'.format(SOFTWARE_1))
         with generate_serials_file(size) as temp_file:
-            args = [COURSE_1, SOFTWARE_1, temp_file.name]
+            args = [self.course_id, SOFTWARE_1, temp_file.name]
             call_command('import_serial_numbers', *args)
 
         log.debug('Adding one set of serials for {0}'.format(SOFTWARE_2))
         with generate_serials_file(size) as temp_file:
-            args = [COURSE_1, SOFTWARE_2, temp_file.name]
+            args = [self.course_id, SOFTWARE_2, temp_file.name]
             call_command('import_serial_numbers', *args)
 
         log.debug('There should be only 2 course-software entries')
@@ -156,7 +164,7 @@ class CommandTest(TestCase):
 
         log.debug('Adding more serial numbers to {0}'.format(SOFTWARE_1))
         with generate_serials_file(size) as temp_file:
-            args = [COURSE_1, SOFTWARE_1, temp_file.name]
+            args = [self.course_id, SOFTWARE_1, temp_file.name]
             call_command('import_serial_numbers', *args)
 
         log.debug('There should be still only 2 course-software entries')
@@ -179,7 +187,7 @@ class CommandTest(TestCase):
         with NamedTemporaryFile() as tmpfile:
             tmpfile.write('\n'.join(known_serials))
             tmpfile.flush()
-            args = [COURSE_1, SOFTWARE_1, tmpfile.name]
+            args = [self.course_id, SOFTWARE_1, tmpfile.name]
             call_command('import_serial_numbers', *args)
 
         log.debug('Check if we added only the new ones')
diff --git a/lms/envs/test.py b/lms/envs/test.py
index 5eb96c8df0b20dadeb76d9343d9886cd4e4f1c56..58b49f744bf3bbc2c5c1fde990d9f302547e2fa8 100644
--- a/lms/envs/test.py
+++ b/lms/envs/test.py
@@ -91,7 +91,7 @@ MODULESTORE = {
 DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': PROJECT_ROOT / "db" / "mitx.db",
+        'NAME': TEST_ROOT / 'db' / 'mitx.db'
     },
 
 }
@@ -122,7 +122,7 @@ CACHES = {
         'LOCATION': '/var/tmp/mongo_metadata_inheritance',
         'TIMEOUT': 300,
         'KEY_FUNCTION': 'util.memcache.safe_key',
-    } 
+    }
 }
 
 # Dummy secret key for dev
diff --git a/lms/templates/courseware/gradebook.html b/lms/templates/courseware/gradebook.html
index fb750aed1947859835e75b1c501a2cdd3ba5b930..015004ee1cdf4501cd6804fa7622a0972a054617 100644
--- a/lms/templates/courseware/gradebook.html
+++ b/lms/templates/courseware/gradebook.html
@@ -13,9 +13,12 @@
   <%static:css group='course'/>
 
   <style type="text/css">
-  .grade_A {color:green;}
-  .grade_B {color:Chocolate;}
-  .grade_C {color:DarkSlateGray;}
+  % for (grade, _), color in zip(ordered_grades, ['green', 'Chocolate']):
+  .grade_${grade} {color:${color};}
+  % endfor
+  % for (grade, _) in ordered_grades[2:]:
+  .grade_${grade} {color:DarkSlateGray;}
+  % endfor
   .grade_F {color:DimGray;}
   .grade_None {color:LightGray;}
   </style>
@@ -78,8 +81,8 @@
             letter_grade = 'None'
             if fraction > 0:
               letter_grade = 'F'
-              for grade in ['A', 'B', 'C']:
-                if fraction >= course.grade_cutoffs[grade]:
+              for (grade, cutoff) in ordered_grades:
+                if fraction >= cutoff:
                   letter_grade = grade
                   break
 
@@ -90,11 +93,11 @@
 
         <tbody>
           %for student in students:
-          <tr>          
+          <tr>
             %for section in student['grade_summary']['section_breakdown']:
               ${percent_data( section['percent'] )}
             %endfor
-            <td>${percent_data( student['grade_summary']['percent'])}</td>
+            ${percent_data( student['grade_summary']['percent'])}
           </tr>
           %endfor
         </tbody>
diff --git a/requirements.txt b/requirements.txt
index 1a383e6cc0c6c8cc38d9ac78d40c9e700dfd041c..a626ac1944d186f9bdd5ecf3b3cd89e52dfa1bac 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,9 +4,7 @@ beautifulsoup==3.2.1
 boto==2.6.0
 django-celery==3.0.11
 django-countries==1.5
-django-debug-toolbar-mongo
 django-followit==0.0.3
-django-jasmine==0.3.2
 django-keyedcache==1.4-6
 django-kombu==0.9.4
 django-mako==0.1.5pre
@@ -19,29 +17,20 @@ django-ses==0.4.1
 django-storages==1.1.5
 django-threaded-multihost==1.4-1
 django==1.4.3
-django_debug_toolbar
-django_nose==1.1
-dogapi==1.2.1
-dogstatsd-python==0.2.1
-factory_boy
 feedparser==5.1.3
 fs==0.4.0
 GitPython==0.3.2.RC1
 glob2==0.3
 http://sympy.googlecode.com/files/sympy-0.7.1.tar.gz
-ipython==0.13.1
 lxml==3.0.1
 mako==0.7.3
 Markdown==2.2.1
-mock==0.8.0
 MySQL-python==1.2.4c1
 networkx==1.7
-newrelic==1.8.0.13
 nltk==2.0.4
-nosexcover==1.0.7
 numpy==1.6.2
 paramiko==1.9.0
-path.py
+path.py==3.0.1
 Pillow==1.7.8
 pip
 pygments==1.5
@@ -51,11 +40,37 @@ python-memcached==1.48
 python-openid==2.2.5
 pytz==2012h
 PyYAML==3.10
-rednose==0.3
 requests==0.14.2
 scipy==0.11.0
 Shapely==1.2.16
 sorl-thumbnail==11.12
 South==0.7.6
-sphinx==1.1.3
 xmltodict==0.4.1
+
+# Used for debugging
+ipython==0.13.1
+
+
+# Metrics gathering and monitoring
+dogapi==1.2.1
+dogstatsd-python==0.2.1
+newrelic==1.8.0.13
+
+# Used for documentation gathering
+sphinx==1.1.3
+
+# Used for testing
+coverage==3.6
+factory_boy==1.3.0
+lettuce==0.2.15
+mock==0.8.0
+nosexcover==1.0.7
+pep8==1.4.5
+pylint==0.27.0
+rednose==0.3
+selenium==2.31.0
+splinter==0.5.0
+django_nose==1.1
+django-jasmine==0.3.2
+django_debug_toolbar
+django-debug-toolbar-mongo
diff --git a/test-requirements.txt b/test-requirements.txt
deleted file mode 100644
index d9db89f1077d0d38513306bc1134f4ca7691bf62..0000000000000000000000000000000000000000
--- a/test-requirements.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-coverage==3.6
-pylint==0.27.0
-pep8==1.4.5
-lettuce==0.2.15
-selenium==2.31.0
-splinter==0.5.0
-