Merge pull request #15951 from edx/jmbowman/ddt_cleanup

ddt usage cleanup
This commit is contained in:
Jeremy Bowman
2017-09-06 11:19:39 -04:00
committed by GitHub
28 changed files with 431 additions and 212 deletions

View File

@@ -183,6 +183,9 @@ class MockTransformer(BlockStructureTransformer):
def transform(self, usage_info, block_structure):
pass
def __repr__(self):
return self.name()
class MockFilteringTransformer(FilteringTransformerMixin, BlockStructureTransformer):
"""

View File

@@ -46,10 +46,18 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
"""
TODAY = timezone.now()
LAST_MONTH = TODAY - datetime.timedelta(days=30)
LAST_WEEK = TODAY - datetime.timedelta(days=7)
NEXT_WEEK = TODAY + datetime.timedelta(days=7)
NEXT_MONTH = TODAY + datetime.timedelta(days=30)
LAST_MONTH = 'last_month'
LAST_WEEK = 'last_week'
NEXT_WEEK = 'next_week'
NEXT_MONTH = 'next_month'
DATES = {
'default_start_date': DEFAULT_START_DATE,
LAST_MONTH: TODAY - datetime.timedelta(days=30),
LAST_WEEK: TODAY - datetime.timedelta(days=7),
NEXT_WEEK: TODAY + datetime.timedelta(days=7),
NEXT_MONTH: TODAY + datetime.timedelta(days=30),
None: None,
}
COURSE_OVERVIEW_TABS = {'courseware', 'info', 'textbooks', 'discussion', 'wiki', 'progress'}
@@ -229,7 +237,7 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
},
{
# # Don't set display name
"start": DEFAULT_START_DATE, # Default start and end dates
"start": 'default_start_date', # Default start and end dates
"end": None,
"advertised_start": None, # No advertised start
"pre_requisite_courses": [], # No pre-requisites
@@ -251,10 +259,15 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
modulestore_type (ModuleStoreEnum.Type): type of store to create the
course in.
"""
kwargs = course_kwargs.copy()
kwargs['start'] = self.DATES[course_kwargs['start']]
kwargs['end'] = self.DATES[course_kwargs['end']]
if 'announcement' in course_kwargs:
kwargs['announcement'] = self.DATES[course_kwargs['announcement']]
# Note: We specify a value for 'run' here because, for some reason,
# .create raises an InvalidKeyError if we don't (even though my
# other test functions don't specify a run but work fine).
course = CourseFactory.create(default_store=modulestore_type, run="TestRun", **course_kwargs)
course = CourseFactory.create(default_store=modulestore_type, run="TestRun", **kwargs)
self.check_course_overview_against_course(course)
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)

View File

@@ -35,6 +35,12 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
VALID_DUE_DATE = datetime.now(pytz.UTC) + timedelta(days=20)
EXPIRED_DUE_DATE = datetime.now(pytz.UTC) - timedelta(days=20)
DATES = {
'valid': VALID_DUE_DATE,
'expired': EXPIRED_DUE_DATE,
None: None,
}
def setUp(self):
super(TestMinGradedRequirementStatus, self).setUp()
self.course = CourseFactory.create(
@@ -85,13 +91,13 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
self.assertEqual(req_status[0]['reason'], expected_reason)
@ddt.data(
(0.6, VALID_DUE_DATE),
(0.6, 'valid'),
(0.52, None),
)
@ddt.unpack
def test_min_grade_requirement_with_valid_grade(self, grade, due_date):
def test_min_grade_requirement_with_valid_grade(self, grade, due_date_name):
"""Test with valid grades submitted before deadline"""
self.assert_requirement_status(grade, due_date, 'satisfied')
self.assert_requirement_status(grade, self.DATES[due_date_name], 'satisfied')
def test_grade_changed(self):
""" Verify successive calls to update a satisfied grade requirement are recorded. """
@@ -106,12 +112,12 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
@ddt.data(
(0.50, None),
(0.51, None),
(0.40, VALID_DUE_DATE),
(0.40, 'valid'),
)
@ddt.unpack
def test_min_grade_requirement_failed_grade_valid_deadline(self, grade, due_date):
def test_min_grade_requirement_failed_grade_valid_deadline(self, grade, due_date_name):
"""Test with failed grades and deadline is still open or not defined."""
self.assert_requirement_status(grade, due_date, None)
self.assert_requirement_status(grade, self.DATES[due_date_name], None)
def test_min_grade_requirement_failed_grade_expired_deadline(self):
"""Test with failed grades and deadline expire"""

View File

@@ -29,6 +29,10 @@ COMMAND_MODULE = 'openedx.core.djangoapps.programs.management.commands.backpopul
class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsApiConfigMixin, TestCase):
"""Tests for the backpopulate_program_credentials management command."""
course_run_key, alternate_course_run_key = (generate_course_run_key() for __ in range(2))
# Constants for the _get_programs_data hierarchy types used in test_flatten()
SEPARATE_PROGRAMS = 'separate_programs'
SEPARATE_COURSES = 'separate_courses'
SAME_COURSE = 'same_course'
def setUp(self):
super(BackpopulateProgramCredentialsTests, self).setUp()
@@ -44,6 +48,54 @@ class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsAp
catalog_integration = self.create_catalog_integration()
UserFactory(username=catalog_integration.service_username)
def _get_programs_data(self, hierarchy_type):
"""
Generate a mock response for get_programs() with the given type of
course hierarchy. Dramatically simplifies (and makes consistent
between test runs) the ddt-generated test_flatten methods.
"""
if hierarchy_type == self.SEPARATE_PROGRAMS:
return [
ProgramFactory(
courses=[
CourseFactory(course_runs=[
CourseRunFactory(key=self.course_run_key),
]),
]
),
ProgramFactory(
courses=[
CourseFactory(course_runs=[
CourseRunFactory(key=self.alternate_course_run_key),
]),
]
),
]
elif hierarchy_type == self.SEPARATE_COURSES:
return [
ProgramFactory(
courses=[
CourseFactory(course_runs=[
CourseRunFactory(key=self.course_run_key),
]),
CourseFactory(course_runs=[
CourseRunFactory(key=self.alternate_course_run_key),
]),
]
),
]
else: # SAME_COURSE
return [
ProgramFactory(
courses=[
CourseFactory(course_runs=[
CourseRunFactory(key=self.course_run_key),
CourseRunFactory(key=self.alternate_course_run_key),
]),
]
),
]
@ddt.data(True, False)
def test_handle(self, commit, mock_task, mock_get_programs):
"""
@@ -112,49 +164,10 @@ class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsAp
# The task should be called for both users since professional and no-id-professional are equivalent.
mock_task.assert_has_calls([mock.call(self.alice.username), mock.call(self.bob.username)])
@ddt.data(
[
ProgramFactory(
courses=[
CourseFactory(course_runs=[
CourseRunFactory(key=course_run_key),
]),
]
),
ProgramFactory(
courses=[
CourseFactory(course_runs=[
CourseRunFactory(key=alternate_course_run_key),
]),
]
),
],
[
ProgramFactory(
courses=[
CourseFactory(course_runs=[
CourseRunFactory(key=course_run_key),
]),
CourseFactory(course_runs=[
CourseRunFactory(key=alternate_course_run_key),
]),
]
),
],
[
ProgramFactory(
courses=[
CourseFactory(course_runs=[
CourseRunFactory(key=course_run_key),
CourseRunFactory(key=alternate_course_run_key),
]),
]
),
],
)
def test_handle_flatten(self, data, mock_task, mock_get_programs):
@ddt.data(SEPARATE_PROGRAMS, SEPARATE_COURSES, SAME_COURSE)
def test_handle_flatten(self, hierarchy_type, mock_task, mock_get_programs):
"""Verify that program structures are flattened correctly."""
mock_get_programs.return_value = data
mock_get_programs.return_value = self._get_programs_data(hierarchy_type)
GeneratedCertificateFactory(
user=self.alice,

View File

@@ -53,11 +53,11 @@ class RegistrationValidationViewTests(test_utils.ApiTestCase):
)
@ddt.data(
['name', (name for name in testutils.VALID_NAMES)],
['email', (email for email in testutils.VALID_EMAILS)],
['password', (password for password in testutils.VALID_PASSWORDS)],
['username', (username for username in testutils.VALID_USERNAMES)],
['country', (country for country in testutils.VALID_COUNTRIES)]
['name', [name for name in testutils.VALID_NAMES]],
['email', [email for email in testutils.VALID_EMAILS]],
['password', [password for password in testutils.VALID_PASSWORDS]],
['username', [username for username in testutils.VALID_USERNAMES]],
['country', [country for country in testutils.VALID_COUNTRIES]]
)
@ddt.unpack
def test_positive_validation_decision(self, form_field_name, user_data):
@@ -71,11 +71,11 @@ class RegistrationValidationViewTests(test_utils.ApiTestCase):
@ddt.data(
# Skip None type for invalidity checks.
['name', (name for name in testutils.INVALID_NAMES[1:])],
['email', (email for email in testutils.INVALID_EMAILS[1:])],
['password', (password for password in testutils.INVALID_PASSWORDS[1:])],
['username', (username for username in testutils.INVALID_USERNAMES[1:])],
['country', (country for country in testutils.INVALID_COUNTRIES[1:])]
['name', [name for name in testutils.INVALID_NAMES[1:]]],
['email', [email for email in testutils.INVALID_EMAILS[1:]]],
['password', [password for password in testutils.INVALID_PASSWORDS[1:]]],
['username', [username for username in testutils.INVALID_USERNAMES[1:]]],
['country', [country for country in testutils.INVALID_COUNTRIES[1:]]]
)
@ddt.unpack
def test_negative_validation_decision(self, form_field_name, user_data):

View File

@@ -6,6 +6,8 @@ import ddt
from django.contrib.messages.middleware import MessageMiddleware
from django.test import RequestFactory, TestCase
from common.test.utils import normalize_repr
from openedx.core.djangolib.markup import HTML, Text
from student.tests.factories import UserFactory
@@ -60,10 +62,10 @@ class UserMessagesTestCase(TestCase):
self.assertEquals(messages[0].icon_class, expected_icon_class)
@ddt.data(
(PageLevelMessages.register_error_message, UserMessageType.ERROR),
(PageLevelMessages.register_info_message, UserMessageType.INFO),
(PageLevelMessages.register_success_message, UserMessageType.SUCCESS),
(PageLevelMessages.register_warning_message, UserMessageType.WARNING),
(normalize_repr(PageLevelMessages.register_error_message), UserMessageType.ERROR),
(normalize_repr(PageLevelMessages.register_info_message), UserMessageType.INFO),
(normalize_repr(PageLevelMessages.register_success_message), UserMessageType.SUCCESS),
(normalize_repr(PageLevelMessages.register_warning_message), UserMessageType.WARNING),
)
@ddt.unpack
def test_message_type(self, register_message_function, expected_message_type):

View File

@@ -16,7 +16,16 @@ from xblock.fields import ScopeIds, UNIQUE_ID, NO_CACHE_VALUE
from xblock.runtime import Runtime
def attribute_pair_repr(self):
"""
Custom string representation for the AttributePair namedtuple which is
consistent between test runs.
"""
return '<AttributePair name={}>'.format(self.name)
AttributePair = namedtuple("AttributePair", ["name", "value"])
AttributePair.__repr__ = attribute_pair_repr
ID_ATTR_NAMES = ("discussion_id", "id",)