class TestComposeClasslistDialogue: @pytest.mark.parametrize( 'class_data, create_blank_class', [ ([Class.from_dict(test_full_class_data_set['json_dict_rep'])], []), ([Class(name='Empty class with no students') ], [True]), # Empty class created pytest.param( [Class(name='class with no students')], [], marks=pytest.mark.xfail( reason="No values left to return from blank_class_dialogue." )), ( [ Class(name=Class.from_dict( test_full_class_data_set['json_dict_rep']).name ), # Empty class, then full class. Class.from_dict(test_full_class_data_set['json_dict_rep']) ], [False]), ([Class(name="Empty class"), Class(name="Empty class") ], [False, True]), # Empty class refused then accepted. ]) def test_compose_classlist_dialogue_full_class( self, monkeypatch, class_data, create_blank_class, test_full_class, ): class_data_to_return = (test_class for test_class in class_data) def mocked_take_class_data_input(class_name): if class_name not in [ test_class.name for test_class in class_data ]: raise ValueError return next(class_data_to_return) blank_class_returns = (chosen_option for chosen_option in create_blank_class) def mocked_blank_class_dialogue(): # Will raise StopIteration if no values left to return (or none to begin with). return next(blank_class_returns) def mocked_class_data_feedback(test_class): if test_class not in class_data: raise ValueError monkeypatch.setattr(class_functions, 'take_class_data_input', mocked_take_class_data_input) monkeypatch.setattr(class_functions, 'blank_class_dialogue', mocked_blank_class_dialogue) monkeypatch.setattr(class_functions, 'class_data_feedback', mocked_class_data_feedback) assert compose_classlist_dialogue( class_data[0].name).json_dict() == class_data[-1].json_dict()
def test_load_class(self, request, database_backend, class_data): """Class loaded has same data as that saved in db, with class/student ids.""" test_database = request.getfixturevalue(database_backend) preexisting_class = request.getfixturevalue(class_data) # Create test NewClass object, mocking avatar_files. preexisting_class = NewClass.from_dict(preexisting_class.json_dict()) for student in preexisting_class: if student.avatar_id: Path(preexisting_class.temp_avatars_dir, student.avatar_id).write_text(student.avatar_id) # Create class in db: test_database.create_class(preexisting_class) # Find class id, load class to verify: classes = test_database.get_classes() test_full_class_id = classes[ 0].id # As the only class will be first item. # Loaded class will have ids: test_loaded_class_with_student_ids = Class.from_dict( preexisting_class.json_dict()) if isinstance(test_database, JSONDatabase): for student in test_loaded_class_with_student_ids.students: student.id = student.name if not isinstance(test_database, JSONDatabase): # This should be accurate for most sql databases. for test_id, student in enumerate( test_loaded_class_with_student_ids.students, start=1): student.id = test_id assert test_database.load_class(test_full_class_id).json_dict( ) == test_loaded_class_with_student_ids.json_dict()
class TestClassStr: @pytest.mark.parametrize( 'class_object,' 'expected_str', [ (Class(name='name_only_class'), f"Class {'name_only_class'}, with id=None, containing 0 students." ), (Class(class_id='some class id', name='name_only_class with id'), f"Class {'name_only_class with id'}, with id={'some class id'}, containing 0 students." ), (Class.from_dict(test_full_class_data_set['json_dict_rep']), f"Class {Class.from_dict(test_full_class_data_set['json_dict_rep']).name}, " f"with id={Class.from_dict(test_full_class_data_set['json_dict_rep']).id}, " f"containing {len(Class.from_dict(test_full_class_data_set['json_dict_rep']).students)} students, " f"with names: {', '.join([student.name for student in Class.from_dict(test_full_class_data_set['json_dict_rep']).students])}." ), (Class(class_id='some class id', name='small_class with id', students=[Student(name='one'), Student(name='two') ]), f"Class {'small_class with id'}, " f"with id={'some class id'}, " f"containing 2 students, " f"with names: one, two."), ]) def test_str(self, class_object, expected_str): assert str(class_object) == expected_str
def test_create_class(self, request, empty_sqlite_database, class_data): test_database = empty_sqlite_database test_class_data = request.getfixturevalue(class_data) # Create test NewClass object, mocking avatar_files. test_class = NewClass.from_dict(test_class_data.json_dict()) for student in test_class: if student.avatar_id: Path(test_class.temp_avatars_dir, student.avatar_id).write_text(student.avatar_id) # no students or class in empty db: assert not test_database._connection().cursor().execute( """SELECT * FROM class""").fetchall() assert not test_database._connection().cursor().execute( """SELECT * FROM student""").fetchall() # Create class in db: assert test_database.create_class(test_class) is None # Find class id, load class to verify: classes = test_database.get_classes() test_class_id = classes[0].id # Class will have ids: test_loaded_class_with_student_ids = Class.from_dict( test_class.json_dict()).json_dict() for test_id, student in enumerate( test_loaded_class_with_student_ids['students'], start=1): student['id'] = test_id assert test_database.load_class( # NB Returned object will be Class, not NewClass: test_class_id).json_dict() == test_loaded_class_with_student_ids
class TestAssembleChartData: @pytest.mark.parametrize('class_from_create_class', [Class.from_dict(test_full_class_data_set['json_dict_rep']), # Pass in test_class NewClass.from_dict(test_full_class_data_set['json_dict_rep']) # NewClass obj ]) def test_assemble_chart_data(self, monkeypatch, class_from_create_class): test_chart_name = 'my_chart' test_chart_filename = test_chart_name mock_score_avatar_dict = {'scores': 'list of avatars'} mock_chart_params = {'some': 'chart_params'} def mocked_take_score_data(class_obj): assert class_obj is class_from_create_class return mock_score_avatar_dict def mocked_take_chart_name(): return test_chart_name def mocked_clean_for_filename(chart_name): assert chart_name == test_chart_name return test_chart_name def mocked_set_chart_params(): return mock_chart_params monkeypatch.setattr(create_chart, 'take_score_data', mocked_take_score_data) monkeypatch.setattr(create_chart, 'take_chart_name', mocked_take_chart_name) monkeypatch.setattr(create_chart, 'clean_for_filename', mocked_clean_for_filename) monkeypatch.setattr(create_chart, 'set_chart_params', mocked_set_chart_params) assert assemble_chart_data(class_from_create_class) == ( test_chart_name, test_chart_filename, mock_score_avatar_dict, mock_chart_params)
def test_take_score_data(self, monkeypatch): test_class = Class.from_dict(test_full_class_data_set['json_dict_rep']) mock_score_avatar_dict = {'scores': 'list of avatars'} def mocked_take_student_scores(scored_class): assert scored_class == test_class return mock_score_avatar_dict monkeypatch.setattr(take_chart_data_UI, 'take_student_scores', mocked_take_student_scores) assert take_score_data(test_class) == mock_score_avatar_dict
class TestClassRepr: @pytest.mark.parametrize('class_object', [ Class(name='name_only_class'), Class(name='name only class with id', class_id='a class id'), Class.from_dict(test_full_class_data_set['json_dict_rep']), ]) def test_repr(self, class_object): assert repr(class_object) == ( f'{class_object.__class__.__module__}.{class_object.__class__.__name__}(' f'id={class_object.id if class_object.id else None !r}, ' f'name={class_object._name!r}, ' f'path_safe_name={class_object._path_safe_name!r}, ' f'students={class_object.students!r}' f')')
def setUp(self): self.test_class = Class.from_dict( test_full_class_data_set['json_dict_rep']) self.score_entry_instruction = ( f"\nEnter student scores for {self.test_class.name}: \n" f"Type score for each student, or '_' to exclude student, and press enter." ) self.newline_after_entry = '\n' self.print_calls = [ self.score_entry_instruction, self.newline_after_entry ] self.mock_score_avatar_dict = {'scores': 'list of avatars'}
def test_assemble_chart_data(self, monkeypatch): test_class = Class.from_dict(test_full_class_data_set['json_dict_rep']) test_class_name = test_class.name test_chart_name = 'my_chart' test_chart_filename = test_chart_name mock_score_avatar_dict = {'scores': 'list of avatars'} mock_chart_params = {'some': 'chart_params'} def mocked_select_classlist(): return test_class_name def mocked_load_class_from_disk(class_name): if class_name != test_class_name: raise ValueError return test_class def mocked_take_score_data(class_obj): if class_obj is not test_class: raise ValueError return mock_score_avatar_dict def mocked_take_chart_name(): return test_chart_name def mocked_clean_for_filename(chart_name): if chart_name != test_chart_name: raise ValueError return test_chart_name def mocked_set_chart_params(): return mock_chart_params monkeypatch.setattr(create_chart, 'select_classlist', mocked_select_classlist) monkeypatch.setattr(create_chart, 'load_class_from_disk', mocked_load_class_from_disk) monkeypatch.setattr(create_chart, 'take_score_data', mocked_take_score_data) monkeypatch.setattr(create_chart, 'take_chart_name', mocked_take_chart_name) monkeypatch.setattr(create_chart, 'clean_for_filename', mocked_clean_for_filename) monkeypatch.setattr(create_chart, 'set_chart_params', mocked_set_chart_params) assert assemble_chart_data() == (test_class_name, test_chart_name, test_chart_filename, mock_score_avatar_dict, mock_chart_params)
def setUp(self): self.mock_CLASSLIST_DATA_PATH = Path('.') self.mock_CLASSLIST_DATA_FILE_TYPE = '.class_data_file' self.test_class_json_dict = test_full_class_data_set['json_dict_rep'] self.test_class_name = self.test_class_json_dict['name'] self.test_class_object = Class.from_dict(self.test_class_json_dict) # Build save file path self.test_class_filename = self.test_class_name + self.mock_CLASSLIST_DATA_FILE_TYPE self.test_class_data_path = self.mock_CLASSLIST_DATA_PATH.joinpath( self.test_class_name) self.test_class_data_file_path = self.test_class_data_path.joinpath( self.test_class_filename)
def test_take_student_scores(self, monkeypatch): """ Score data returned. Test with generic database. """ test_class = Class.from_dict(test_full_class_data_set['json_dict_rep']) # Gives no score to one student with and without an avatar. mocked_take_score_entry_return_values = [ 0, 1, 3, None, 50, 99, 100, 1, 2, 3, 4, None, 6, 7, 8 ] test_take_student_scores_return_value = { 0: [test_class.students[0]], # Cali 1: [ test_class.students[1], # Monty test_class.students[7] ], # Regina 3: [ test_class.students[2], # Abby test_class.students[9] ], # Alex # No score, not returned: None: [test_class.students[3], # Zach # test_class.students[11]], # Edgar 50: [test_class.students[4]], # Janell 99: [test_class.students[5]], # Matthew 100: [test_class.students[6]], # Olivia 2: [test_class.students[8]], # Ashley 4: [test_class.students[10]], # Melissa 6: [test_class.students[12]], # Danielle 7: [test_class.students[13]], # Kayla 8: [test_class.students[14]], # Jaleigh } score = (score for score in mocked_take_score_entry_return_values) def mocked_take_score_entry(student_name): return next(score) monkeypatch.setattr(take_chart_data_UI, 'take_score_entry', mocked_take_score_entry) assert take_student_scores( test_class) == test_take_student_scores_return_value
def setUp(self): self.mock_DEFAULT_AVATAR_PATH = 'mocked_default_avatar_path' self.test_class = Class.from_dict( test_full_class_data_set['json_dict_rep']) # NB If data other than avatar in dict values, this test implementation may need to change. self.avatar_paths = [ f'path to {student.avatar_filename}' if student.avatar_filename is not None else self.mock_DEFAULT_AVATAR_PATH for student in self.test_class.students ] # Gives no score to one student with and without an avatar. self.mocked_take_score_entry_return_values = [ 0, 1, 3, None, 50, 99, 100, 1, 2, 3, 4, None, 6, 7, 8 ] self.mocked_get_avatar_path_return_values = [ self.avatar_paths[self.mocked_take_score_entry_return_values.index( score)] for score in self.mocked_take_score_entry_return_values if score is not None ] self.test_take_student_scores_return_value = { 0: ['path to Cali_avatar.png'], 1: [self.mock_DEFAULT_AVATAR_PATH, self.mock_DEFAULT_AVATAR_PATH], 3: [self.mock_DEFAULT_AVATAR_PATH, self.mock_DEFAULT_AVATAR_PATH], # No score, not returned: None: ['Zach_avatar.png', None] 50: [self.mock_DEFAULT_AVATAR_PATH], 99: [self.mock_DEFAULT_AVATAR_PATH], 100: [self.mock_DEFAULT_AVATAR_PATH], 2: ['path to Ashley_avatar.png'], 4: [self.mock_DEFAULT_AVATAR_PATH], 6: ['path to Danielle.png'], 7: [self.mock_DEFAULT_AVATAR_PATH], 8: [self.mock_DEFAULT_AVATAR_PATH] }
class TestNewChart: @pytest.mark.parametrize('user_wants_to_save', [True, False], ids=['User saving.', 'User not saving.']) @pytest.mark.parametrize('class_from_create_class', [None, # Test Full class fixture Class.from_dict(test_full_class_data_set['json_dict_rep']), # Pass in test_class NewClass.from_dict(test_full_class_data_set['json_dict_rep']) # NewClass obj ]) def test_new_chart(self, monkeypatch, empty_generic_database, class_from_create_class, test_full_class, user_wants_to_save): # Test class either passed, or instantiated if None is passed. test_class = class_from_create_class or test_full_class test_chart_name = 'test_chart_name' test_chart_default_filename = 'test_chart_default_filename' test_chart_params = {'test_chart_params': 'some chart params'} test_score_avatar_dict = {'test_student_scores': 'test student avatars'} test_chart_data_dict = {'class_id': test_class.id, 'class_name': test_class.name, 'chart_name': test_chart_name, 'chart_default_filename': test_chart_default_filename, 'chart_params': test_chart_params, 'score-students_dict': test_score_avatar_dict, } test_chart_image_location = 'some image location' def mocked_select_classlist(): if class_from_create_class: raise ValueError('select_classlist should not be called if a class is passed.') return test_full_class.name def mocked_get_classes(): if class_from_create_class: raise ValueError('Function should not be called if class is passed.') return test_class.name def mocked_load_class(class_name): if class_from_create_class: raise ValueError('Function should not be called if class is passed.') return test_class def mocked_assemble_chart_data(class_obj): return (test_chart_name, test_chart_default_filename, test_score_avatar_dict, test_chart_params) def mocked_create_chart(chart_data_dict): assert chart_data_dict == test_chart_data_dict def mocked_generate_chart_image(chart_data_dict): assert chart_data_dict == test_chart_data_dict return test_chart_image_location def mocked_show_image(chart_image_location): assert chart_image_location == test_chart_image_location return user_wants_to_save user_save_chart_image_mock = {'called': False} def mocked_user_save_chart_image(chart_data_dict, chart_image_location): assert chart_data_dict == test_chart_data_dict assert chart_image_location == test_chart_image_location user_save_chart_image_mock['called'] = True if not user_wants_to_save: raise ValueError('Should not be called if user did not want to save.') test_database = empty_generic_database test_database.load_class = mocked_load_class test_database.get_classes = mocked_get_classes test_database.create_chart = mocked_create_chart monkeypatch.setattr(definitions, 'DATABASE', test_database) monkeypatch.setattr(create_chart, 'select_classlist', mocked_select_classlist) monkeypatch.setattr(create_chart, 'assemble_chart_data', mocked_assemble_chart_data) monkeypatch.setattr(create_chart, 'generate_chart_image', mocked_generate_chart_image) monkeypatch.setattr(create_chart, 'show_image', mocked_show_image) monkeypatch.setattr(create_chart, 'user_save_chart_image', mocked_user_save_chart_image) assert new_chart(class_from_create_class) is None assert user_save_chart_image_mock['called'] is user_wants_to_save
def test_generate_avatar_coords_generic_api(self, monkeypatch, empty_generic_database): test_database = empty_generic_database test_database.default_avatar_path = 'mocked_default_avatar_path' test_class = Class.from_dict(test_full_class_data_set['json_dict_rep']) student_score_entry_sequence = [ 0, 1, 3, None, 50, 99, 100, 1, 2, 3, 4, None, 6, 7, 8 ] # Mock out avatar_paths with readable strings. avatar_paths = [ f'path to {student.avatar_id}' if student.avatar_id is not None else test_database.default_avatar_path for student in test_class.students ] # Format those avatar_paths to remove students who did not submit a score: mocked_get_avatar_path_return_values = [ avatar_paths[student_score_entry_sequence.index(score)] for score in student_score_entry_sequence if score is not None ] avatar_path = (path for path in mocked_get_avatar_path_return_values) avatar_ids = [student.avatar_id for student in test_class] def mocked_get_avatar_path(avatar_id): """ Strictly speaking does not need a mock, as should raise error, but re-mocking method for clarity. """ assert avatar_id in avatar_ids # deepcode ignore unguarded~next~call: No guard - let error propagate, should not reach StopIteration. return next(avatar_path) test_database.get_avatar_path = mocked_get_avatar_path monkeypatch.setattr(process_chart_data.definitions, 'DATABASE', test_database) test_student_scores_dict = { 0: [test_class.students[0]], # Cali 1: [ test_class.students[1], # Monty test_class.students[7] ], # Regina 3: [ test_class.students[2], # Abby test_class.students[9] ], # Alex # No score, not returned: None: [test_class.students[3], # Zach # test_class.students[11]], # Edgar 50: [test_class.students[4]], # Janell 99: [test_class.students[5]], # Matthew 100: [test_class.students[6]], # Olivia 2: [test_class.students[8]], # Ashley 4: [test_class.students[10]], # Melissa 6: [test_class.students[12]], # Danielle 7: [test_class.students[13]], # Kayla 8: [test_class.students[14]], # Jaleigh } test_generate_avatar_coords_result = { 'mocked_default_avatar_path': [(0.0, 15), (0.0, 25), (0.0, 35), (0.0, 45), (0.0, 55), (0.0, 65), (10.0, 15), (10.0, 25), (50.0, 5), (100.0, 5)], 'path to Ashley_avatar.png': [(100.0, 15)], 'path to Cali_avatar.png': [(0.0, 5)], 'path to Danielle_avatar.png': [(10.0, 5)] } assert generate_avatar_coords( test_student_scores_dict, test_class.id) == test_generate_avatar_coords_result
def mock_from_file(class_path): assert class_path == Path( test_json_database.class_data_path, test_class_name, f'{test_class_name}{test_json_database.class_data_file_type}') return Class.from_dict(test_full_class.json_dict())
def setUp(self) -> None: self.full_class_return = Class.from_dict( test_full_class_data_set['json_dict_rep']) self.empty_class_return = Class(name=self.full_class_return.name)
def setUp(self): self.test_class_json_dict = test_full_class_data_set['json_dict_rep'] self.test_classname = self.test_class_json_dict['name'] self.test_class_object = Class.from_dict(self.test_class_json_dict)