Repository: oppia/oppia

Issue:
Fix tests/methods that refer to private functions in Python
There are currently a few tests that reference private functions in Python. Tests should test through public interface.  If the test tests a private method, find a way to test the method through the existing public interface instead (you'll need to find the corresponding public API function that uses the internal private one, and use that function instead in tests). Please note that modifications to other classes outside of the file in question may be necessary. 

Note that you can identify such references by searching for the string `# pylint: disable=protected-access`. (If you don't see the string in the file(s) you're assigned to, please let the author of this issue know so that the list can be edited accordingly.)

**Important**: In general, do not just change methods from private to public. You'll need to find the already existing publicly-accessible function which invokes the private method and use that one instead.

The following the cases where we test private functions:

- [ ] gae_models.VersionedModel @Azrecycle4
  - delete
  - revert
  - get_version
  - get_multi_versions
  - get_snapshots_metadata
- [ ] suggestion_services_test.SuggestionServicesUnitTests @gopivaibhav
  - test_accept_suggestion_commit_message_after_updating_a_suggestion
  - test_update_translation_suggestion_to_change_translation_html
- [ ] suggestion_services_test.SuggestionIntegrationTests @hardikkat24
  - setUp
- [ ] email_manager_test.EmailRightsTest @vijaykpatel
  - test_sender_id_validation
- [ ] email_manager.BulkEmailsTests @brianlinUM
  - test_that_correct_email_is_sent
  - test_that_exception_is_raised_for_unauthorised_sender
- [ ] library_test.LibraryPageTests @brianlinUM
  - test_library_handler_for_created_explorations 
- [ ] build_test (All) @Mohitbalwani26
- [ ] suggestion_test.SuggestionUnitTests @AnuragVats007
  - setUp
- [ ] exp_services_test.ZipFileExportUnitTests (all) @ayushjaink8

Completed:

- [x] draft_upgrade_services_test.DraftUpgradeUnitTests @kaylahardie
  - test_try_upgrade_success
- [x] draft_upgrade_services_test.DraftUpgradeUtilUnitTests @kaylahardie
  - test_convert_states_v29_dict_to_v30_dict
  - test_convert_states_v28_dict_to_v29_dict
  - test_convert_states_v27_dict_to_v28_dict
- [x] gae_models_test.GenerateHashTests - @lorryaze
  - test_same_inputs_always_gives_same_hashes
  - test_different_inputs_give_different_hashes
- [x] models_test.BaseCalculationUnitTests @donosco98 (#7841)
  - test_equality_of_hashable_answers
- [x] user_services_test.LastLoginIntegrationTests @vpishuk
  - test_legacy_user
- [x] user_services_test.LastExplorationEditedIntegrationTests @vpishuk
  - test_legacy_user
  - test_last_exp_edit_time_gets_updated
- [x] user_services_test.LastExplorationCreatedIntegrationTests @vpishuk
  - test_legacy_user
  - test_last_exp_edit_time_gets_updated
- [x] config_domain_test.ConfigPropertyRegistryTests (@nishantwrp)
  - test_config_property_schemas_are_valid
- [x] email_manager.DuplicateEmailTests @Hudda
  - test_send_email_does_not_resend_if_same_hash_exists
  - test_sending_email_with_different_recipient_but_same_hash
  - test_sending_email_with_different_subject_but_same_hash
  - test_sending_email_with_different_body_but_same_hash
  - test_duplicate_emails_are_sent_after_some_time_has_elapsed
- [x] user_jobs_one_off_test.UserFirstContributionMsecOneOffJobTests - @Arnesh07 
  - test_contribution_msec_does_not_update_on_unpublished_explorations
- [x] user_jobs_one_off_test.UserLastExplorationActivityOneOffJobTests @Hudda
  - test_that_last_created_time_is_updated
- [x] html_validation_service_test.ContentMigrationTests - @cathuang20
  - test_validate_soup_for_rte
  - test_validate_customization_args_in_tag
  - test_that_last_edited_time_is_updated
  - test_that_last_edited_and_created_time_both_updated
  - test_that_last_edited_and_created_time_are_not_updated
- [x] rte_component_registry_test.RteComponentUnitTests - @alexlee311
  - test_rte_components_are_valid
- [x] extensions.interactions.base_test.InteractionUnitTests - @aazuspan
  - test_default_interactions_are_valid
- [x] collection_services_test @sudiptapradhan1181
  - _count_at_least_editable_collection_summaries
- [x] exp_services.test - @aazuspan
  - _count_at_least_editable_exploration_summaries


Discussion:
Can I try to do this?
Sure, which part would you like to do? (each major bullet point is open)
@kevinlee12 perhaps swap the bullet points with check-boxes?
good call, done!
Hi can I take **jobs_test.JobManagerUnitTests** ? 
sure, go for it!
@ankita240796 could you please add @kevinlee12 as my reviewer? Thank you
I'll go ahead and do a review :)
Hello! I'd like to try: email_manager.BulkEmailsTests
Hi @KristiKovacs, I've assigned you to `email_manager.BulkEmailsTests`. You can start working on it! :) 
Hello @DubeySandeep, I'd like to try "suggestion_services_test.SuggestionServicesUnitTests".
@rahmot, I've assigned you to `suggestion_services_test.SuggestionServicesUnitTests`.
@DubeySandeep, please can you put me through on how to get started with suggestion_services_test.SuggestionServicesUnitTests. Thank you!
@rahmot I would have approached this issue in the following way:

 After going through the details of the issue, I understand that  "Few test methods in test files are using private methods for testing, so I have to change the test methods such that it only uses the public methods."

Let's solve one test method for your case:

1. I looked into suggestion_services_test and found `test_accept_suggestion_handled_suggestion_failure` method on line 350.
2. I see line 368 is calling a private method `_update_suggestion`.
3. I read the test code and found that we are trying to change the status of the suggestion and update the suggestion using the private method from the suggestion_service.
4. Going further I read a few lines of suggestion_service and found `mark_review_completed` which does the same thing and I think that can be useful.

With all this information I will go ahead and change the test method and create a PR!

(Note: maybe this idea won't work, then you have to take a deeper look and find another way to solve the problem.)

I hope this helps.
@DubeySandeep, thanks for the illumination. I sincerely appreciate.
Hello @DubeySandeep can I take email_manager.DuplicateEmailTests ?
Hi @Hudda, I think you haven't filled the survey form yet, would you mind doing that first? Once you complete the instructions provided [here](https://github.com/oppia/oppia/wiki/Contributing-code-to-Oppia#setting-things-up), do let us know we will go ahead and assign you to the issue you have selcted. :) 
@DubeySandeep I have adhered to your instructions and filled the form.
@Hudda, I've assigned you `email_manager.DuplicateEmailTests`, you can go ahead and start working on it! :)  
Hello I am new to the python contribution world.  I would like to tactle 
email_manager_test.EmailRightsTest
How would I go about starting to do this task?
welcome @emzee831! Please take a look at our [contributor's guide](https://github.com/oppia/oppia/wiki/Contributing-code-to-Oppia#setting-things-up) on how to get started.
Hi @rahmot, any updates here? Please feel free to let us know if you are stuck somewhere, we will be happy to help you! Thanks!
> welcome @emzee831! Please take a look at our [contributor's guide](https://github.com/oppia/oppia/wiki/Contributing-code-to-Oppia#setting-things-up) on how to get started.

Just looked over the contribution guide and filled out all the information. 
Hi, @DubeySandeep  can I work on  jobs_test.JobRegistryTests ?
HI! I'm an Outreachy applicant interested in the 'print-to-pdf' project. Can I work on  draft_upgrade_services_test.DraftUpgradeUtilUnitTests ?
@obulat You got it!
@donosco98 Assigned you jobs_test.JobRegistryTests :D
@DubeySandeep can I work on other parts of this issue? I have already completed one part.
@Hudda, which one would you like?
@kevinlee12 user_jobs_one_off_test.UserLastExplorationActivityOneOffJobTests will be fine.
Hi @Hudda, I've assigned you to `user_jobs_one_off_test.UserLastExplorationActivityOneOffJobTests`, you can go ahead and start working on it! :) 
Hi @rahmot, any updates on this? Please do let me know if you are stuck somewhere and I will be happy to help. Thanks!
Hi @KristiKovacs, any update on the assigned issue?
@DubeySandeep can I work on  models_test.BaseCalculationUnitTests .
Hi @donosco98,  I think you haven't filled the survey form yet, would you mind doing that first? Once you complete the instructions provided [here](https://github.com/oppia/oppia/wiki/Contributing-code-to-Oppia#setting-things-up), do let us know we will go ahead and assign you to the issue you have selected. :) 
@DubeySandeep , I've Filled the survey
Hello, I’m still working on it but I’ll need some help. I’ll email you
later today with some updates/questions.

On Thu, Oct 24, 2019 at 4:15 PM Sandeep Dubey <notifications@github.com>
wrote:

> Hi @KristiKovacs <https://github.com/KristiKovacs>, any update on the
> assigned issue?
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/oppia/oppia/issues/7450?email_source=notifications&email_token=ALDNA7YV7SKECUPWEZINWGTQQH66LA5CNFSM4IPFH6PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOECGJKNA#issuecomment-546084148>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ALDNA73CVHTR3ROUBGHMN7TQQH66LANCNFSM4IPFH6PA>
> .
>

@donosco98, Thanks for filling the survey report, I've assigned you to `models_test.BaseCalculationUnitTests` so you can go ahead and start working on it! :) 

Resolving patch:
diff --git a/extensions/answer_summarizers/models.py b/extensions/answer_summarizers/models.py
--- a/extensions/answer_summarizers/models.py
+++ b/extensions/answer_summarizers/models.py
@@ -62,7 +62,7 @@
 ])
 
 
-class _HashableAnswer(python_utils.OBJECT):
+class HashableAnswer(python_utils.OBJECT):
     """Wraps answer with object that can be placed into sets and dicts."""
 
     def __init__(self, answer):
@@ -73,7 +73,7 @@ def __hash__(self):
         return hash(self.hashable_answer)
 
     def __eq__(self, other):
-        if isinstance(other, _HashableAnswer):
+        if isinstance(other, HashableAnswer):
             return self.hashable_answer == other.hashable_answer
         return False
 
@@ -92,7 +92,7 @@ def _get_top_answers_by_frequency(answers, limit=None):
     Returns:
         stats_domain.AnswerFrequencyList. A list of the top "limit" answers.
     """
-    answer_counter = utils.OrderedCounter(_HashableAnswer(a) for a in answers)
+    answer_counter = utils.OrderedCounter(HashableAnswer(a) for a in answers)
     return stats_domain.AnswerFrequencyList([
         stats_domain.AnswerOccurrence(hashable_answer.answer, frequency)
         for hashable_answer, frequency in answer_counter.most_common(n=limit)
@@ -125,10 +125,10 @@ def _get_top_unresolved_answers_by_frequency(
     # classification categorization of each answer.
     for ans in answers_with_classification:
         frequency = 0
-        if _HashableAnswer(ans['answer']) in classification_results_dict:
-            frequency = classification_results_dict[_HashableAnswer(
+        if HashableAnswer(ans['answer']) in classification_results_dict:
+            frequency = classification_results_dict[HashableAnswer(
                 ans['answer'])]['frequency']
-        classification_results_dict[_HashableAnswer(ans['answer'])] = {
+        classification_results_dict[HashableAnswer(ans['answer'])] = {
             'classification_categorization': (
                 ans['classification_categorization']),
             'frequency': frequency + 1


Test patch:
diff --git a/extensions/answer_summarizers/models_test.py b/extensions/answer_summarizers/models_test.py
--- a/extensions/answer_summarizers/models_test.py
+++ b/extensions/answer_summarizers/models_test.py
@@ -33,9 +33,9 @@ def test_requires_override_for_calculation(self):
                 state_answers_dict={})
 
     def test_equality_of_hashable_answers(self):
-        hashable_answer_1 = answer_models._HashableAnswer('answer_1')  # pylint: disable=protected-access
-        hashable_answer_2 = answer_models._HashableAnswer('answer_2')  # pylint: disable=protected-access
-        hashable_answer_3 = answer_models._HashableAnswer('answer_1')  # pylint: disable=protected-access
+        hashable_answer_1 = answer_models.HashableAnswer('answer_1')
+        hashable_answer_2 = answer_models.HashableAnswer('answer_2')
+        hashable_answer_3 = answer_models.HashableAnswer('answer_1')
 
         self.assertFalse(hashable_answer_1 == hashable_answer_2)
         self.assertTrue(hashable_answer_1 == hashable_answer_3)