From 1cfff02f064728b24d32f97a531df43d4b6375d7 Mon Sep 17 00:00:00 2001 From: "J. Cliff Dyer" Date: Thu, 6 Oct 2016 09:46:23 -0400 Subject: [PATCH] Add tests for scoring of diverse problem types. Includes: * CAPA * ORA * LTI * LTI-Consumer * SGA * Randomized Content Block TNL-5692 TNL-5464 --- common/test/data/blocks/capa.xml | 6 - common/test/data/blocks/coderesponse.xml | 25 --- common/test/data/blocks/library_content.xml | 4 - common/test/data/blocks/lti.xml | 1 - common/test/data/blocks/openassessment.xml | 87 -------- common/test/data/scoreable/README | 43 ++++ .../test/data/scoreable/about/overview.html | 48 ++++ common/test/data/scoreable/assets/assets.xml | 1 + .../test/data/scoreable/chapter/chapter1.xml | 3 + common/test/data/scoreable/course.xml | 1 + common/test/data/scoreable/course/course.xml | 4 + .../library_content/library_content.xml | 5 + .../test/data/scoreable/lti/library_lti.xml | 1 + common/test/data/scoreable/lti/lti.xml | 1 + .../test/data/scoreable/policies/assets.json | 1 + .../policies/course/grading_policy.json | 14 ++ .../scoreable/policies/course/policy.json | 64 ++++++ common/test/data/scoreable/problem/capa.xml | 11 + .../data/scoreable/problem/library_mc.xml | 13 ++ .../data/scoreable/problem/library_text.xml | 9 + .../data/scoreable/sequential/sequential1.xml | 9 + .../scoreable/vertical/vertical1_capa.xml | 3 + .../data/scoreable/vertical/vertical2_ora.xml | 92 ++++++++ .../data/scoreable/vertical/vertical3_sga.xml | 3 + .../data/scoreable/vertical/vertical4_lti.xml | 3 + .../vertical/vertical5_lti_consumer.xml | 3 + .../vertical/vertical6_library_content.xml | 3 + .../scoreable/vertical/vertical7_dndv2.xml | 3 + lms/djangoapps/grades/tests/test_new.py | 211 ++++++++++-------- lms/djangoapps/grades/tests/utils.py | 10 + openedx/core/lib/xblock_utils/test_utils.py | 26 --- 31 files changed, 472 insertions(+), 236 deletions(-) delete mode 100644 common/test/data/blocks/capa.xml delete mode 100644 common/test/data/blocks/coderesponse.xml delete mode 100644 common/test/data/blocks/library_content.xml delete mode 100644 common/test/data/blocks/lti.xml delete mode 100644 common/test/data/blocks/openassessment.xml create mode 100644 common/test/data/scoreable/README create mode 100644 common/test/data/scoreable/about/overview.html create mode 100644 common/test/data/scoreable/assets/assets.xml create mode 100644 common/test/data/scoreable/chapter/chapter1.xml create mode 100644 common/test/data/scoreable/course.xml create mode 100644 common/test/data/scoreable/course/course.xml create mode 100644 common/test/data/scoreable/library_content/library_content.xml create mode 100644 common/test/data/scoreable/lti/library_lti.xml create mode 100644 common/test/data/scoreable/lti/lti.xml create mode 100644 common/test/data/scoreable/policies/assets.json create mode 100644 common/test/data/scoreable/policies/course/grading_policy.json create mode 100644 common/test/data/scoreable/policies/course/policy.json create mode 100644 common/test/data/scoreable/problem/capa.xml create mode 100644 common/test/data/scoreable/problem/library_mc.xml create mode 100644 common/test/data/scoreable/problem/library_text.xml create mode 100644 common/test/data/scoreable/sequential/sequential1.xml create mode 100644 common/test/data/scoreable/vertical/vertical1_capa.xml create mode 100644 common/test/data/scoreable/vertical/vertical2_ora.xml create mode 100644 common/test/data/scoreable/vertical/vertical3_sga.xml create mode 100644 common/test/data/scoreable/vertical/vertical4_lti.xml create mode 100644 common/test/data/scoreable/vertical/vertical5_lti_consumer.xml create mode 100644 common/test/data/scoreable/vertical/vertical6_library_content.xml create mode 100644 common/test/data/scoreable/vertical/vertical7_dndv2.xml delete mode 100644 openedx/core/lib/xblock_utils/test_utils.py diff --git a/common/test/data/blocks/capa.xml b/common/test/data/blocks/capa.xml deleted file mode 100644 index a3460b1ef8..0000000000 --- a/common/test/data/blocks/capa.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/common/test/data/blocks/coderesponse.xml b/common/test/data/blocks/coderesponse.xml deleted file mode 100644 index fde73aa327..0000000000 --- a/common/test/data/blocks/coderesponse.xml +++ /dev/null @@ -1,25 +0,0 @@ - - -

- ESTIMATED TIME TO COMPLETE: 4 minutes -

-
-      >>> print testList
-      [1, 16, 64, 81]
-    
-
- - - - -# Your Code Here - - -def square(a): - return a * a -applyToEach(testList, square) - - {"grader": "finger_exercises/L6/applyToEach3/grade_ate3.py"} - - -
diff --git a/common/test/data/blocks/library_content.xml b/common/test/data/blocks/library_content.xml deleted file mode 100644 index 6c8fc8105b..0000000000 --- a/common/test/data/blocks/library_content.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/common/test/data/blocks/lti.xml b/common/test/data/blocks/lti.xml deleted file mode 100644 index ae8bed35c1..0000000000 --- a/common/test/data/blocks/lti.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/common/test/data/blocks/openassessment.xml b/common/test/data/blocks/openassessment.xml deleted file mode 100644 index 12b0919ba6..0000000000 --- a/common/test/data/blocks/openassessment.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - Censorship in the Libraries - - 'All of us can think of a book that we hope none of our children or any - other children have taken off the shelf. But if I have the right to remove - that book from the shelf -- that work I abhor -- then you also have exactly - the same right and so does everyone else. And then we have no books left on - the shelf for any of us.' --Katherine Paterson, Author - - Write a persuasive essay to a newspaper reflecting your views on censorship - in libraries. Do you believe that certain materials, such as books, music, - movies, magazines, etc., should be removed from the shelves if they are - found offensive? Support your position with convincing arguments from your - own experience, observations, and/or reading. - - Read for conciseness, clarity of thought, and form. - - - Ideas - Determine if there is a unifying theme or main idea. - - - - - - Content - Assess the content of the submission - - - - - - - diff --git a/common/test/data/scoreable/README b/common/test/data/scoreable/README new file mode 100644 index 0000000000..91fbc7170a --- /dev/null +++ b/common/test/data/scoreable/README @@ -0,0 +1,43 @@ +This course fixture provides a representative sample of scoreable block types. + +## Contents of the Course + +It contains several scoreable blocks in one sequence: + +- CAPA +- ORA +- SGA (Staff Graded Assignment) +- LTI +- LTI Consumer +- Randomized Content Block (containing Library Content with CAPA problems) +- Drag and Drop (v2) + +## Adding Blocks + +To expand coverage to other block types, you can either edit the course xml +directly, or do the following: + +1. Zip up the scoreable directory into a tarball: + + $ tar cvzf course.tar.gz common/test/data/scoreable' + +2. Import the tarball into studio. +3. Add the new blocks. +4. Export the modified course. +5. Unzip to a temporary directory: + + $ cd /tmp + $ tar xvzf ~/Downloads/course.*.tar.gz + $ somewhere, and + +6. Copy the data back into the test directory: + + $ rsync -avz --delete-after /tmp/course/ /path/to/edx-platform/common/test/data/scoreable + +## Use in Tests + +As of this writing, this course is used in +`lms/djangoapps/grades/tests/test_new.py` If you modify the course, you may +need to adjust the values for `SCORED_BLOCK_COUNT` and `ACTUAL_TOTAL_POSSIBLE` +in `TestMultipleProblemBlockTypes` to reflect the real number of scoreable +blocks. diff --git a/common/test/data/scoreable/about/overview.html b/common/test/data/scoreable/about/overview.html new file mode 100644 index 0000000000..f73257e8a5 --- /dev/null +++ b/common/test/data/scoreable/about/overview.html @@ -0,0 +1,48 @@ +
+

About This Course

+

Include your long course description here. The long course description should contain 150-400 words.

+ +

This is paragraph 2 of the long course description. Add more paragraphs as needed. Make sure to enclose them in paragraph tags.

+
+ +
+

Requirements

+

Add information about the skills and knowledge students need to take this course.

+
+ +
+

Course Staff

+
+
+ Course Staff Image #1 +
+ +

Staff Member #1

+

Biography of instructor/staff member #1

+
+ +
+
+ Course Staff Image #2 +
+ +

Staff Member #2

+

Biography of instructor/staff member #2

+
+
+ +
+
+

Frequently Asked Questions

+
+

What web browser should I use?

+

The Open edX platform works best with current versions of Chrome, Firefox or Safari, or with Internet Explorer version 9 and above.

+

See our list of supported browsers for the most up-to-date information.

+
+ +
+

Question #2

+

Your answer would be displayed here.

+
+
+
diff --git a/common/test/data/scoreable/assets/assets.xml b/common/test/data/scoreable/assets/assets.xml new file mode 100644 index 0000000000..b3e36a3191 --- /dev/null +++ b/common/test/data/scoreable/assets/assets.xml @@ -0,0 +1 @@ + diff --git a/common/test/data/scoreable/chapter/chapter1.xml b/common/test/data/scoreable/chapter/chapter1.xml new file mode 100644 index 0000000000..60294e1b2c --- /dev/null +++ b/common/test/data/scoreable/chapter/chapter1.xml @@ -0,0 +1,3 @@ + + + diff --git a/common/test/data/scoreable/course.xml b/common/test/data/scoreable/course.xml new file mode 100644 index 0000000000..a8843f71de --- /dev/null +++ b/common/test/data/scoreable/course.xml @@ -0,0 +1 @@ + diff --git a/common/test/data/scoreable/course/course.xml b/common/test/data/scoreable/course/course.xml new file mode 100644 index 0000000000..9d8e138a21 --- /dev/null +++ b/common/test/data/scoreable/course/course.xml @@ -0,0 +1,4 @@ + + + + diff --git a/common/test/data/scoreable/library_content/library_content.xml b/common/test/data/scoreable/library_content/library_content.xml new file mode 100644 index 0000000000..923ffc8971 --- /dev/null +++ b/common/test/data/scoreable/library_content/library_content.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/common/test/data/scoreable/lti/library_lti.xml b/common/test/data/scoreable/lti/library_lti.xml new file mode 100644 index 0000000000..f700de683e --- /dev/null +++ b/common/test/data/scoreable/lti/library_lti.xml @@ -0,0 +1 @@ + diff --git a/common/test/data/scoreable/lti/lti.xml b/common/test/data/scoreable/lti/lti.xml new file mode 100644 index 0000000000..5c7349757f --- /dev/null +++ b/common/test/data/scoreable/lti/lti.xml @@ -0,0 +1 @@ + diff --git a/common/test/data/scoreable/policies/assets.json b/common/test/data/scoreable/policies/assets.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/common/test/data/scoreable/policies/assets.json @@ -0,0 +1 @@ +{} diff --git a/common/test/data/scoreable/policies/course/grading_policy.json b/common/test/data/scoreable/policies/course/grading_policy.json new file mode 100644 index 0000000000..1c35bc14dd --- /dev/null +++ b/common/test/data/scoreable/policies/course/grading_policy.json @@ -0,0 +1,14 @@ +{ + "GRADER": [ + { + "drop_count": 0, + "min_count": 1, + "short_label": "Prob", + "type": "Problems", + "weight": 1.0 + } + ], + "GRADE_CUTOFFS": { + "Pass": 0.5 + } +} diff --git a/common/test/data/scoreable/policies/course/policy.json b/common/test/data/scoreable/policies/course/policy.json new file mode 100644 index 0000000000..fd82e523ab --- /dev/null +++ b/common/test/data/scoreable/policies/course/policy.json @@ -0,0 +1,64 @@ +{ + "course/course": { + "advanced_modules": [ + "lti", + "lti_consumer", + "library_content", + "split_test", + "conditional", + "randomize", + "drag-and-drop-v2", + "library", + "videosequence", + "problemset", + "acid_parent", + "done", + "wrapper", + "edx_sga", + "bookmarks" + ], + "cert_html_view_enabled": true, + "discussion_topics": { + "General": { + "id": "course" + } + }, + "display_name": "Introduction to problems.", + "graceperiod": "", + "language": "en", + "minimum_grade_credit": 0.8, + "start": "2030-01-01T00:00:00Z", + "tabs": [ + { + "name": "Home", + "type": "course_info" + }, + { + "name": "Course", + "type": "courseware" + }, + { + "name": "Textbooks", + "type": "textbooks" + }, + { + "name": "Discussion", + "type": "discussion" + }, + { + "name": "Wiki", + "type": "wiki" + }, + { + "name": "Progress", + "type": "progress" + } + ], + "xml_attributes": { + "filename": [ + "course/course.xml", + "course/course.xml" + ] + } + } +} diff --git a/common/test/data/scoreable/problem/capa.xml b/common/test/data/scoreable/problem/capa.xml new file mode 100644 index 0000000000..2bbd6805a8 --- /dev/null +++ b/common/test/data/scoreable/problem/capa.xml @@ -0,0 +1,11 @@ + + +

This is the first problem.

+

>> What kind of swallow?

+ + African + European + I don't know + +
+
diff --git a/common/test/data/scoreable/problem/library_mc.xml b/common/test/data/scoreable/problem/library_mc.xml new file mode 100644 index 0000000000..0f7a86647b --- /dev/null +++ b/common/test/data/scoreable/problem/library_mc.xml @@ -0,0 +1,13 @@ + + +

This is a multiple choice version of the question.

+ + Hint: It's not Microsoft. + + Enron + Microsoft + edX + J. C. Penney + +
+
diff --git a/common/test/data/scoreable/problem/library_text.xml b/common/test/data/scoreable/problem/library_text.xml new file mode 100644 index 0000000000..812da24c4f --- /dev/null +++ b/common/test/data/scoreable/problem/library_text.xml @@ -0,0 +1,9 @@ + + +

This is the first problem

+ + Hint: It's not Microsoft. + + +
+
diff --git a/common/test/data/scoreable/sequential/sequential1.xml b/common/test/data/scoreable/sequential/sequential1.xml new file mode 100644 index 0000000000..790b0beaf0 --- /dev/null +++ b/common/test/data/scoreable/sequential/sequential1.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/common/test/data/scoreable/vertical/vertical1_capa.xml b/common/test/data/scoreable/vertical/vertical1_capa.xml new file mode 100644 index 0000000000..74465b15ae --- /dev/null +++ b/common/test/data/scoreable/vertical/vertical1_capa.xml @@ -0,0 +1,3 @@ + + + diff --git a/common/test/data/scoreable/vertical/vertical2_ora.xml b/common/test/data/scoreable/vertical/vertical2_ora.xml new file mode 100644 index 0000000000..f1bf6d884b --- /dev/null +++ b/common/test/data/scoreable/vertical/vertical2_ora.xml @@ -0,0 +1,92 @@ + + + Open Response Assessment + + + + + Replace this text with your own sample response for this assignment. Then, under Response Score to the right, select an option for each criterion. Learners practice performing peer assessments by assessing this response and comparing the options that they select in the rubric with the options that you specified. + + + + + + Replace this text with another sample response, and then specify the options that you would select for this response. + + + + + + + + + + + + Censorship in the Libraries + + 'All of us can think of a book that we hope none of our children or any other children have taken off the shelf. But if I have the right to remove that book from the shelf -- that work I abhor -- then you also have exactly the same right and so does everyone else. And then we have no books left on the shelf for any of us.' --Katherine Paterson, Author + + Write a persuasive essay to a newspaper reflecting your views on censorship in libraries. Do you believe that certain materials, such as books, music, movies, magazines, etc., should be removed from the shelves if they are found offensive? Support your position with convincing arguments from your own experience, observations, and/or reading. + + Read for conciseness, clarity of thought, and form. + + + + + + Ideas + + Determine if there is a unifying theme or main idea. + + + + + + Content + + Assess the content of the submission + + + + + + +(Optional) What aspects of this response stood out to you? What did it do well? How could it be improved? + + +I think that this response... + + + + diff --git a/common/test/data/scoreable/vertical/vertical3_sga.xml b/common/test/data/scoreable/vertical/vertical3_sga.xml new file mode 100644 index 0000000000..546fabe620 --- /dev/null +++ b/common/test/data/scoreable/vertical/vertical3_sga.xml @@ -0,0 +1,3 @@ + + + diff --git a/common/test/data/scoreable/vertical/vertical4_lti.xml b/common/test/data/scoreable/vertical/vertical4_lti.xml new file mode 100644 index 0000000000..9100c8bedf --- /dev/null +++ b/common/test/data/scoreable/vertical/vertical4_lti.xml @@ -0,0 +1,3 @@ + + + diff --git a/common/test/data/scoreable/vertical/vertical5_lti_consumer.xml b/common/test/data/scoreable/vertical/vertical5_lti_consumer.xml new file mode 100644 index 0000000000..dc68985b57 --- /dev/null +++ b/common/test/data/scoreable/vertical/vertical5_lti_consumer.xml @@ -0,0 +1,3 @@ + + + diff --git a/common/test/data/scoreable/vertical/vertical6_library_content.xml b/common/test/data/scoreable/vertical/vertical6_library_content.xml new file mode 100644 index 0000000000..cb04fe98a1 --- /dev/null +++ b/common/test/data/scoreable/vertical/vertical6_library_content.xml @@ -0,0 +1,3 @@ + + + diff --git a/common/test/data/scoreable/vertical/vertical7_dndv2.xml b/common/test/data/scoreable/vertical/vertical7_dndv2.xml new file mode 100644 index 0000000000..36de405dfc --- /dev/null +++ b/common/test/data/scoreable/vertical/vertical7_dndv2.xml @@ -0,0 +1,3 @@ + + + diff --git a/lms/djangoapps/grades/tests/test_new.py b/lms/djangoapps/grades/tests/test_new.py index 3db290014c..372d940e31 100644 --- a/lms/djangoapps/grades/tests/test_new.py +++ b/lms/djangoapps/grades/tests/test_new.py @@ -8,6 +8,8 @@ import datetime import ddt from django.conf import settings from django.db.utils import DatabaseError +import itertools + from mock import patch import pytz @@ -16,16 +18,17 @@ from courseware.tests.helpers import get_request_for_user from courseware.tests.test_submitting_problems import ProblemSubmissionTestMixin from lms.djangoapps.course_blocks.api import get_course_blocks from lms.djangoapps.grades.config.tests.utils import persistent_grades_feature_flags -from openedx.core.lib.xblock_utils.test_utils import add_xml_block_from_file from student.models import CourseEnrollment from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory +from xmodule.modulestore.tests.utils import TEST_DATA_DIR +from xmodule.modulestore.xml_importer import import_course_from_xml from ..models import PersistentSubsectionGrade from ..new.course_grade import CourseGradeFactory from ..new.subsection_grade import SubsectionGrade, SubsectionGradeFactory -from .utils import mock_get_score +from .utils import mock_get_score, mock_get_submissions_score class GradeTestBase(SharedModuleStoreTestCase): @@ -239,20 +242,20 @@ class SubsectionGradeTest(GradeTestBase): @ddt.ddt -class TestMultipleProblemTypesSubsectionScores(ModuleStoreTestCase, ProblemSubmissionTestMixin): +class TestMultipleProblemTypesSubsectionScores(SharedModuleStoreTestCase): """ Test grading of different problem types. """ - default_problem_metadata = { - u'graded': True, - u'weight': 2.5, - u'max_score': 7.0, - u'due': datetime.datetime(2099, 3, 15, 12, 30, 0, tzinfo=pytz.utc), - } + SCORED_BLOCK_COUNT = 7 + ACTUAL_TOTAL_POSSIBLE = 16.0 - COURSE_NAME = u'Problem Type Test Course' - COURSE_NUM = u'probtype' + @classmethod + def setUpClass(cls): + super(TestMultipleProblemTypesSubsectionScores, cls).setUpClass() + cls.load_scoreable_course() + chapter1 = cls.course.get_children()[0] + cls.seq1 = chapter1.get_children()[0] def setUp(self): super(TestMultipleProblemTypesSubsectionScores, self).setUp() @@ -260,39 +263,104 @@ class TestMultipleProblemTypesSubsectionScores(ModuleStoreTestCase, ProblemSubmi self.student = UserFactory.create(is_staff=False, username=u'test_student', password=password) self.client.login(username=self.student.username, password=password) self.request = get_request_for_user(self.student) - self.course = CourseFactory.create( - display_name=self.COURSE_NAME, - number=self.COURSE_NUM - ) - self.chapter = ItemFactory.create( - parent=self.course, - category=u'chapter', - display_name=u'Test Chapter' - ) - self.seq1 = ItemFactory.create( - parent=self.chapter, - category=u'sequential', - display_name=u'Test Sequential 1', - graded=True - ) - self.vert1 = ItemFactory.create( - parent=self.seq1, - category=u'vertical', - display_name=u'Test Vertical 1' + self.course_structure = get_course_blocks(self.student, self.course.location) + + @classmethod + def load_scoreable_course(cls): + """ + This test course lives at `common/test/data/scoreable`. + + For details on the contents and structure of the file, see + `common/test/data/scoreable/README`. + """ + + course_items = import_course_from_xml( + cls.store, + 'test_user', + TEST_DATA_DIR, + source_dirs=['scoreable'], + static_content_store=None, + target_id=cls.store.make_course_key('edX', 'scoreable', '3000'), + raise_on_failure=True, + create_if_not_present=True, ) - def _get_fresh_subsection_score(self, course_structure, subsection): - """ - Return a Score object for the specified subsection. + cls.course = course_items[0] - Ensures that a stale cached value is not returned. - """ + def test_score_submission_for_all_problems(self): subsection_factory = SubsectionGradeFactory( self.student, + course_structure=self.course_structure, + course=self.course, + ) + score = subsection_factory.create(self.seq1) + + self.assertEqual(score.all_total.earned, 0.0) + self.assertEqual(score.all_total.possible, self.ACTUAL_TOTAL_POSSIBLE) + + # Choose arbitrary, non-default values for earned and possible. + earned_per_block = 3.0 + possible_per_block = 7.0 + with mock_get_submissions_score(earned_per_block, possible_per_block) as mock_score: + # Configure one block to return no possible score, the rest to return 3.0 earned / 7.0 possible + block_count = self.SCORED_BLOCK_COUNT - 1 + mock_score.side_effect = itertools.chain( + [(earned_per_block, None, earned_per_block, None)], + itertools.repeat(mock_score.return_value) + ) + score = subsection_factory.update(self.seq1) + self.assertEqual(score.all_total.earned, earned_per_block * block_count) + self.assertEqual(score.all_total.possible, possible_per_block * block_count) + + +@ddt.ddt +class TestVariedMetadata(ProblemSubmissionTestMixin, ModuleStoreTestCase): + """ + Test that changing the metadata on a block has the desired effect on the + persisted score. + """ + default_problem_metadata = { + u'graded': True, + u'weight': 2.5, + u'due': datetime.datetime(2099, 3, 15, 12, 30, 0, tzinfo=pytz.utc), + } + + def setUp(self): + super(TestVariedMetadata, self).setUp() + self.course = CourseFactory.create() + self.chapter = ItemFactory.create( + parent=self.course, + category="chapter", + display_name="Test Chapter" + ) + self.sequence = ItemFactory.create( + parent=self.chapter, + category='sequential', + display_name="Test Sequential 1", + graded=True + ) + self.vertical = ItemFactory.create( + parent=self.sequence, + category='vertical', + display_name='Test Vertical 1' + ) + self.problem_xml = u''' + + + + + + + ''' + self.request = get_request_for_user(UserFactory()) + self.client.login(username=self.request.user.username, password="test") + CourseEnrollment.enroll(self.request.user, self.course.id) + course_structure = get_course_blocks(self.request.user, self.course.location) + self.subsection_factory = SubsectionGradeFactory( + self.request.user, course_structure=course_structure, course=self.course, ) - return subsection_factory.update(subsection) def _get_altered_metadata(self, alterations): """ @@ -303,48 +371,28 @@ class TestMultipleProblemTypesSubsectionScores(ModuleStoreTestCase, ProblemSubmi metadata.update(alterations) return metadata - def _get_score_with_alterations(self, alterations): + def _add_problem_with_alterations(self, alterations): """ - Given a dict of alterations to the default_problem_metadata, return - the score when one correct problem (out of two) is submitted. + Add a problem to the course with the specified metadata alterations. """ + metadata = self._get_altered_metadata(alterations) + ItemFactory.create( + parent=self.vertical, + category="problem", + display_name="problem", + data=self.problem_xml, + metadata=metadata, + ) - add_xml_block_from_file(u'problem', u'capa.xml', parent=self.vert1, metadata=metadata) - course_structure = get_course_blocks(self.student, self.course.location) + def _get_score(self): + """ + Return the score of the test problem when one correct problem (out of + two) is submitted. + """ self.submit_question_answer(u'problem', {u'2_1': u'Correct'}) - return self._get_fresh_subsection_score(course_structure, self.seq1) - - def test_score_submission_for_capa_problems(self): - add_xml_block_from_file(u'problem', u'capa.xml', parent=self.vert1, metadata=self.default_problem_metadata) - course_structure = get_course_blocks(self.student, self.course.location) - - score = self._get_fresh_subsection_score(course_structure, self.seq1) - self.assertEqual(score.all_total.earned, 0.0) - self.assertEqual(score.all_total.possible, 2.5) - - self.submit_question_answer(u'problem', {u'2_1': u'Correct'}) - score = self._get_fresh_subsection_score(course_structure, self.seq1) - self.assertEqual(score.all_total.earned, 1.25) - self.assertEqual(score.all_total.possible, 2.5) - - @ddt.data( - (u'openassessment', u'openassessment.xml'), - (u'coderesponse', u'coderesponse.xml'), - (u'lti', u'lti.xml'), - (u'library_content', u'library_content.xml'), - ) - @ddt.unpack - def test_loading_different_problem_types(self, block_type, filename): - """ - Test that transformation works for various block types - """ - metadata = self.default_problem_metadata.copy() - if block_type == u'library_content': - # Library content does not have a weight - del metadata[u'weight'] - add_xml_block_from_file(block_type, filename, parent=self.vert1, metadata=metadata) + return self.subsection_factory.create(self.sequence) @ddt.data( ({}, 1.25, 2.5), @@ -355,7 +403,8 @@ class TestMultipleProblemTypesSubsectionScores(ModuleStoreTestCase, ProblemSubmi ) @ddt.unpack def test_weight_metadata_alterations(self, alterations, expected_earned, expected_possible): - score = self._get_score_with_alterations(alterations) + self._add_problem_with_alterations(alterations) + score = self._get_score() self.assertEqual(score.all_total.earned, expected_earned) self.assertEqual(score.all_total.possible, expected_possible) @@ -365,23 +414,11 @@ class TestMultipleProblemTypesSubsectionScores(ModuleStoreTestCase, ProblemSubmi ) @ddt.unpack def test_graded_metadata_alterations(self, alterations, expected_earned, expected_possible): - score = self._get_score_with_alterations(alterations) + self._add_problem_with_alterations(alterations) + score = self._get_score() self.assertEqual(score.graded_total.earned, expected_earned) self.assertEqual(score.graded_total.possible, expected_possible) - @ddt.data( - {u'max_score': 99.3}, - {u'max_score': 1.0}, - {u'max_score': 0.0}, - {u'max_score': None}, - ) - def test_max_score_does_not_change_results(self, alterations): - expected_earned = 1.25 - expected_possible = 2.5 - score = self._get_score_with_alterations(alterations) - self.assertEqual(score.all_total.earned, expected_earned) - self.assertEqual(score.all_total.possible, expected_possible) - class TestCourseGradeLogging(SharedModuleStoreTestCase): """ diff --git a/lms/djangoapps/grades/tests/utils.py b/lms/djangoapps/grades/tests/utils.py index 0d640100bd..2bc038f5c8 100644 --- a/lms/djangoapps/grades/tests/utils.py +++ b/lms/djangoapps/grades/tests/utils.py @@ -28,6 +28,16 @@ def mock_get_score(earned=0, possible=1): yield mock_score +@contextmanager +def mock_get_submissions_score(earned=0, possible=1): + """ + Mocks the _get_submissions_score function to return the specified values + """ + with patch('lms.djangoapps.grades.scores._get_score_from_submissions') as mock_score: + mock_score.return_value = (earned, possible, earned, possible) + yield mock_score + + def answer_problem(course, request, problem, score=1, max_value=1): """ Records a correct answer for the given problem. diff --git a/openedx/core/lib/xblock_utils/test_utils.py b/openedx/core/lib/xblock_utils/test_utils.py deleted file mode 100644 index ac1b145dc0..0000000000 --- a/openedx/core/lib/xblock_utils/test_utils.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Utilities for testing xblocks -""" - -from django.conf import settings - -from xmodule.modulestore.tests.factories import ItemFactory - -TEST_DATA_DIR = settings.COMMON_ROOT / u'test/data' - - -def add_xml_block_from_file(block_type, filename, parent, metadata): - """ - Create a block of the specified type with content included from the - specified XML file. - - XML filenames are relative to common/test/data/blocks. - """ - with open(TEST_DATA_DIR / u'blocks' / filename) as datafile: - return ItemFactory.create( - parent=parent, - category=block_type, - data=datafile.read().decode('utf-8'), - metadata=metadata, - display_name=u'problem' - )