Repository: oppia/oppia

Issue:
Refactor: Updating subparts of activity to accept object as a parameter instead of dict.
We expect a domain object should only accept dict for the class method `from_dict()` and we should avoid accepting dict for updating subparts of the object. In the following section we have curated a list of methods that needs to be refactored:

**Subtopic**: @RafayGhafoor
- [x] update_page_contents_html
- [x] update_page_contents_audio

**State**:
- [x] update_content @RafayGhafoor
- [x] update_param_changes @RafayGhafoor
- [x] update_interaction_customization_args @sauravpratihar
- [x] update_interaction_default_outcome @florinbalint
- [x] update_interaction_hints  @florinbalint
- [x] update_interaction_answer_groups @Nik-09 
- [x] update_interaction_solution @anmol1point0

**Question**:
- [x] update_question_state_data @teddymarchildon

**Skill**:
- [x] update_explanation @Vinit-Dantkale
- [x] update_worked_examples @nadintamer
- [x] add_misconception @kiriakivel

-------------------
You can follow these steps to complete the work (for each method):
- Find the place where the method is defined. (*_domain.py where * is the name of the class.)
- Change the method such that it accepts objects instead of dict (Also, do keep a record of how it's converting the dict to object as you'll be using this code later.).
- Update the docstring of the method.
- Find all files where this method is used.
- Update the caller to firstly convert the dict into a domain object, and only then send the domain object as a parameter.
- Look for the test file where this method is used and update the test files accordingly (you can use `git grep` to find all the cases where this method is used.)
- Try running tests for the changed test files.
- Once the tests pass, you can create a PR!

Pr's for reference: #8582 #8591 #8699 #7024 

Note: For a guide on how to access Oppia's webpages, see [this](https://github.com/oppia/oppia/wiki/How-to-access-Oppia-webpages).


Discussion:
Assigning `State` part of this issue to @rakshitkumarcse. 
Assigned @suicide11 for 'Skill' part of this issue!
@DubeySandeep  Hi, Could you assign me subtopic? Thanks!
Hi @MLA98, I've assigned you to the `subtopic` part of this issue. 

/cc @nithusha21 (mentor)
@DubeySandeep Can you assign me the State part ? Thanks
Hi @PiyushBhangale, would you mind filling up the contribution survey form (asked [here](https://github.com/oppia/oppia/wiki/Contributing-code-to-Oppia#setting-things-up))? Once done you can start with the `question` part of this issue. Thanks! :)
@suicide11, any update on this issue? 
Un-assigned  @suicide11 from this issue as per discssion on email.  
@MLA98, any update on this issue? 
@DubeySandeep Hi, can you assign me to the Skill part of this issue? Thanks!
@chetan-rns, assigned you. Go ahead and feel free to make PR.
Thanks
And did you filled up the contribution survey form (asked [here](https://github.com/oppia/oppia/wiki/Contributing-code-to-Oppia#setting-things-up)) 
@import-keshav Thanks. Yes, I've filled the contribution survey form.
@PiyushBhangale any updates ?
@DubeySandeep Hi, sorry I don't have time recently to do it. Please unassigned me. Thank you.
@DubeySandeep, @import-keshav, Can you assign me the state part?
@RafayGhafoor Assigned you, Go Ahead.
Thanks.
Hi! Can you assign me a subtopic?
@anamika2004 Assigned you, go ahead.
Thanks.
Is there any work still un-assigned that I can fix up on this issue? Please assign me if so :)
Unassigned @PiyushBhangale from `question` part of this issue because of lack of activity. /cc @brianrodri 
Assigned @teddymarchildon to the `question` part of this issue. /cc @bansalnitish 
@RafayGhafoor are you working on all of the methods of `state` part of this issue? It's okay to do a few parts in your first PR! Do let me know if you want to do a few parts, I'll go ahead and assign you for the same. Thanks!
@DubeySandeep, Alright, I will make pull request for few parts, thanks. 
Hey I'll be glad to have one of these issues assigned to me if there are any left.
Hi @worstahrieu,  I've assigned you to the last 2 methods of the state_domain part of this issue. You can start working on it. :)

@RafayGhafoor, I'm not sure about which few methods you are working right now but I've assigned you to the first 2 methods, I hope that's fine! (do let me know if it's not okay!)   
@DubeySandeep Can you assign me an issue if there is anything left. I would like to start contributing to that.

Hi @puneeth2001, I've assigned you to the answer_groups and default_outcome part of the state structure (see the issue header comment). You can start working on it :) /cc @ankita240796 

Thanks!
@RafayGhafoor, @anamika2004, @worstahrieu any update on assigned issue?
Unassigned @RafayGhafoor & @worstahrieu for this issue because of lack of activity. (As mentioned by their onboarding mentor.)

@RafayGhafoor & @worstahrieu, feel free to ping me here if you still want to continue working on any part of this issue.

<hr>
EDIT:
Re-assigned @worstahrieu back! (Realized she has started working on the issue and found her on gitter.)
@DubeySandeep, Yes, I am testing the modified code, and updating the documentation. Sorry for the late response. 
Hi Dubey, sorry for the late reply, but I really am working on it, can I
get a bit more time? It's my first issue ever and I sincerely take every
moment of my free time to try to understand the code and learn how to
modify it. If you need this issue resolved quickly, then I won't be able to
do so, but giving me a few more weeks would probably suffice.


On Fri, 19 Jul 2019, 11:42 Sandeep Dubey, <notifications@github.com> wrote:

> Unassigned @RafayGhafoor <https://github.com/RafayGhafoor> & @worstahrieu
> <https://github.com/worstahrieu> for this issue because of lack of
> activity. (As mentioned by their onboarding mentor.)
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/oppia/oppia/issues/6271?email_source=notifications&email_token=AHOFMOK7G7UHHT6MXWIIW33QAGEAFA5CNFSM4GXZCIX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2LEWBQ#issuecomment-513166086>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AHOFMOMIABC5DJLMADDI4D3QAGEAFANCNFSM4GXZCIXQ>
> .
>

@worstahrieu, I've already re-assigned you to the issue after replying to your Gitter message. It's totally fine, take your time and do let us (or your onboarding mentor) know if you need any help. Thanks! :)

@RafayGhafoor, Thanks for the updates. (/cc @apb7 [onboarding mentor])
Hi @anamika2004, could you please let us know is there any update on this issue ? Also, let us know if you're facing any issue regarding the same. 
Hi @DubeySandeep can you assign me **update_interaction_customization_args** ? 
Hi @sauravpratihar, I've assigned you to `update_interaction_customization_args` you can start working on it. :) 
Hi @puneeth2001, How is this doing? Please do let me know if you are facing any issues. Thanks!
Unassigned @anamika2004, because of lack of response. /cc @bansalnitish 
@DubeySandeep, Can you please assign me the remaining subtopics?(update_page_contents_html and update_page_contents_audio)
Hi @RafayGhafoor, I've assigned you to the `subtopics` part of this issue. 
Hi @ankita240796 I am working on it. Thanks for asking
Hi @puneeth2001, Thanks for the update. Do let me know if you need any help.
@DubeySandeep, Subtopic and State parts assigned are completed.
@RafayGhafoor, Thanks a lot for completing it, I've updated the issue! :)  
@DubeySandeep as discussed in mail, we don't need a domain object for update_interaction_customization_args as we save them directly as a dict.
@worstahrieu, any update on the assigned issue?
@chetan-rns, any update on the assigned issue?
still working on it
Hi. Sorry, please un-assign me from the issue. I've spent a lot of time trying to resolve it, but I don't seem to be ready for such task.
Hi @worstahrieu, Thanks for the update, would you link to work on a simple starter issue, I can help you find such issue, let me know if you want so? :) 
Unassigned @puneeth2001 because of lack of response.

@chetan-rns, have you started working on this issue?
@DubeySandeep, Can you please assign me remaining parts of this issue?
@RafayGhafoor, Yeah sure! :)

I've assigned you to remaining parts of this issue, you can check the header comment of this issue. 
Hi Sandeep, I'd be delighted to give it a try with a simpler issue.

On Sat, 28 Sep 2019 at 04:04, Sandeep Dubey <notifications@github.com>
wrote:

> Hi @worstahrieu <https://github.com/worstahrieu>, Thanks for the update,
> would you link to work on a simple starter issue, I can help you find such
> issue, let me know if you want so? :)
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/oppia/oppia/issues/6271?email_source=notifications&email_token=AHOFMOJOYMXU5Q2Q4QVQBXDQL23T7A5CNFSM4GXZCIX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD72OJRI#issuecomment-536143045>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AHOFMON5WNXT7UH6H3ZSLZLQL23T7ANCNFSM4GXZCIXQ>
> .
>


-- 
Adam Borysławski
+48 511 164 669

hello sir, i'm confused what to do here because it's my first time in open source as well as on github. I think i didn't get what to do in this issue it'll be a great help if you guide me through or we start with simpler issue, it'll be a great help

@worstahrieu, What do you think of #7427? You can pick a part of that issue and once you resolve 1-2 issues from #7427 I think you'll have the basic understanding of the oppia backend code structure and you can start working on other issues. I hope this helps! :) 

@DubeySandeep Sure, I'm up for it.
@DubeySandeep Hi, can I work on update_worked_examples? 
Hello, I would like to contribute to this issue. 
Hello, can I work on add_misconception?
@nadintamer I've assigned you!
Hi @lorryaze -- sounds good, I've assigned you! Just to check, it's been a little while since you posted (sorry about that) so I just wanted to check whether you're still interested in working on that one? If not, that's totally OK.
Yes I'm interested, this week I was a little busy with college so I'm not
participating so actively, sorry, as soon as I have time, I will go back to
work on this issue if there is no problem. ;)

Em dom, 10 de nov de 2019 00:02, Sean Lip <notifications@github.com>
escreveu:

> Hi @lorryaze <https://github.com/lorryaze> -- sounds good, I've assigned
> you! Just to check, it's been a little while since you posted (sorry about
> that) so I just wanted to check whether you're still interested in working
> on that one? If not, that's totally OK.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/oppia/oppia/issues/6271?email_source=notifications&email_token=AHG4MFROJN7GLZOHSH5NGQ3QS52TPA5CNFSM4GXZCIX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDUUD5Q#issuecomment-552157686>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AHG4MFROLYMI7POEEH3LFKLQS52TPANCNFSM4GXZCIXQ>
> .
>

Hi @lorryaze, sure! One question though -- do you have some idea of when you plan to get back to it? (This is just to avoid things sitting for too long -- if you're busy for the next block of time, it may be better to deassign you for now and assign you only once you're ready to start working on it.)
I plan to start work this week, but you can deassing me, when I start to work I request a task again and sorry for anything.
OK, sounds good, I'll deassign you for now, but let us know when you'd like to take it up again. Thanks (and no worries)!
Also, hi @emzee831 -- sorry for the late reply! Would you still like to take up one of the open checkboxes? If so please let us know which one, and we can assign you to it. Thanks!
Hello, I'd like to take: update_interaction_answer_groups. @seanlip , Can you assign me this issue? Thanks!


@piotrhm I've assigned you!
Hi! Could I work on update_interaction_hints?
@csmith23 all yours!
@U8NWXD please assign me 
Hi there, can I get the "update_interaction_solution" part ?
Hi @FlorinBalint, I've assigned "update_interaction_solution" to you.
May I take the 'update_interaction_default_outcome' as well ?
It's easier when you have some context.
Sure @FlorinBalint! I've just assigned you. 

Also deassigning @piotrhm @csmith23 @harshita93 due to lack of progress/PRs (/cc @U8NWXD). @FlorinBalint if you'd like to tackle any/all of the other bullet points in your PR, please feel free to do so (just state in this comment thread which ones you are taking up).
I'll add a PR for the 'default_outcome' part, I can take a look at the others.
I'll write a comment here if I start working on one.
I'll start working on the 'update_interaction_hints' part.
Thanks @FlorinBalint -- assigned you!
I can take the _'update_interaction_answer_groups'_ part too.

I was looking at other good starter issues that I could contribute to, I'll need to learn some JS/Angular/Web stuff.
Hi @FlorinBalint, I've assigned "update_interaction_answer_groups" to you.
Hi, I would like to take up **update_explanation** 
@Vinit-Dantkale I've assigned you!
Hi, can someone assign me to add_misconception?
Hi @yudani-s, I've assigned add_misconception to you!
Just to note, I am working on this task together with @robatras. He will probably be the one to submit the pull request.
Hey @yudani-s Seems like @robatras made the PR but didn't get it to merge. What's the status now? 
@FlorinBalint Hey what's up?
@Showtim3 We are no longer working on this task.
@Showtim3 @aks681 can I work on this task please give me a brief explanation of what has been done and what still needs to be done
Hey @anshalshukla You can read the issue description to know what needs to be done and also take a look at the Pr's for reference. These two ought to give you some insight, then comment the method/function which you want to work on from the above list. 
hi, can I work on add_misconception ?
Hi @kiriakivel, I've assigned it to you!
hey, can i work on update_interaction_solution
Yep, assigned it to you @anmol1point0!
hey! can I get assigned to update_interaction_answer_groups?
@kiriakivel Yep, I have assigned you.

Resolving patch:
diff --git a/core/controllers/admin.py b/core/controllers/admin.py
--- a/core/controllers/admin.py
+++ b/core/controllers/admin.py
@@ -293,17 +293,16 @@ def _create_dummy_question(
 
         state.update_recorded_voiceovers(recorded_voiceovers)
         state.update_written_translations(written_translations)
-        solution_dict = (
-            state_domain.Solution(
-                'TextInput', False, 'Solution', state_domain.SubtitledHtml(
-                    'solution', '<p>This is a solution.</p>')).to_dict())
+        solution = state_domain.Solution(
+            'TextInput', False, 'Solution', state_domain.SubtitledHtml(
+                'solution', '<p>This is a solution.</p>'))
         hints_list = [
             state_domain.Hint(
                 state_domain.SubtitledHtml('hint_1', '<p>This is a hint.</p>')
             )
         ]
 
-        state.update_interaction_solution(solution_dict)
+        state.update_interaction_solution(solution)
         state.update_interaction_hints(hints_list)
         state.update_interaction_customization_args({
             'placeholder': 'Enter text here',
diff --git a/core/domain/exp_services.py b/core/domain/exp_services.py
--- a/core/domain/exp_services.py
+++ b/core/domain/exp_services.py
@@ -403,7 +403,11 @@ def apply_change_list(exploration_id, change_list):
                 elif (
                         change.property_name ==
                         exp_domain.STATE_PROPERTY_INTERACTION_SOLUTION):
-                    state.update_interaction_solution(change.new_value)
+                    new_solution = None
+                    if change.new_value is not None:
+                        new_solution = state_domain.Solution.from_dict(
+                            state.interaction.id, change.new_value)
+                    state.update_interaction_solution(new_solution)
                 elif (
                         change.property_name ==
                         exp_domain.STATE_PROPERTY_SOLICIT_ANSWER_DETAILS):
diff --git a/core/domain/state_domain.py b/core/domain/state_domain.py
--- a/core/domain/state_domain.py
+++ b/core/domain/state_domain.py
@@ -1910,15 +1910,14 @@ def update_interaction_hints(self, hints_list):
         self._update_content_ids_in_assets(
             old_content_id_list, new_content_id_list)
 
-    def update_interaction_solution(self, solution_dict):
+    def update_interaction_solution(self, solution):
         """Update the solution of interaction.
 
         Args:
-            solution_dict: dict or None. The dict representation of
-                Solution object.
+            solution: Solution. Object of class Solution.
 
         Raises:
-            Exception: 'solution_dict' is not a dict.
+            Exception: 'solution' is not a domain object.
         """
         old_content_id_list = []
         new_content_id_list = []
@@ -1926,13 +1925,12 @@ def update_interaction_solution(self, solution_dict):
             old_content_id_list.append(
                 self.interaction.solution.explanation.content_id)
 
-        if solution_dict is not None:
-            if not isinstance(solution_dict, dict):
+        if solution is not None:
+            if not isinstance(solution, Solution):
                 raise Exception(
-                    'Expected solution to be a dict, received %s'
-                    % solution_dict)
-            self.interaction.solution = Solution.from_dict(
-                self.interaction.id, solution_dict)
+                    'Expected solution to be a Solution object,recieved %s'
+                    % solution)
+            self.interaction.solution = solution
             new_content_id_list.append(
                 self.interaction.solution.explanation.content_id)
         else:


Test patch:
diff --git a/core/domain/exp_domain_test.py b/core/domain/exp_domain_test.py
--- a/core/domain/exp_domain_test.py
+++ b/core/domain/exp_domain_test.py
@@ -716,15 +716,17 @@ def test_validation(self):
         init_state.update_interaction_answer_groups(answer_groups_list)
         init_state.update_interaction_default_outcome(default_outcome)
         exploration.validate()
-
-        init_state.update_interaction_solution({
+        solution_dict = {
             'answer_is_exclusive': True,
             'correct_answer': 'hello_world!',
             'explanation': {
                 'content_id': 'solution',
                 'html': 'hello_world is a string'
                 }
-        })
+        }
+        solution = state_domain.Solution.from_dict(
+            init_state.interaction.id, solution_dict)
+        init_state.update_interaction_solution(solution)
         self._assert_validation_error(
             exploration,
             re.escape('Hint(s) must be specified if solution is specified'))
@@ -1210,7 +1212,7 @@ def test_get_content_count(self):
         ]
         init_state.update_interaction_hints(hints_list)
 
-        solution = {
+        solution_dict = {
             'answer_is_exclusive': False,
             'correct_answer': 'helloworld!',
             'explanation': {
@@ -1218,6 +1220,8 @@ def test_get_content_count(self):
                 'html': '<p>hello_world is a string</p>'
             },
         }
+        solution = state_domain.Solution.from_dict(
+            init_state.interaction.id, solution_dict)
         init_state.update_interaction_solution(solution)
 
         self.assertEqual(exploration.get_content_count(), 7)
@@ -8328,7 +8332,7 @@ def test_all_html_strings_are_collected(self):
         ]
         state2.update_interaction_hints(hint_list2)
 
-        solution_dict1 = {
+        solution_dict = {
             'interaction_id': '',
             'answer_is_exclusive': True,
             'correct_answer': 'Answer1',
@@ -8337,8 +8341,9 @@ def test_all_html_strings_are_collected(self):
                 'html': '<p>This is solution for state1</p>'
             }
         }
-
-        state1.update_interaction_solution(solution_dict1)
+        solution = state_domain.Solution.from_dict(
+            state1.interaction.id, solution_dict)
+        state1.update_interaction_solution(solution)
 
         answer_group_list2 = [{
             'rule_specs': [{
diff --git a/core/domain/exp_services_test.py b/core/domain/exp_services_test.py
--- a/core/domain/exp_services_test.py
+++ b/core/domain/exp_services_test.py
@@ -4087,6 +4087,18 @@ def test_update_interaction_solutions(self):
         self.assertEqual(
             exploration.init_state.interaction.solution.to_dict(),
             solution)
+        solution = None
+        exp_services.update_exploration(
+            self.albert_id, self.NEW_EXP_ID, [exp_domain.ExplorationChange({
+                'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
+                'property_name': exp_domain.STATE_PROPERTY_INTERACTION_SOLUTION,
+                'state_name': exploration.init_state_name,
+                'new_value': solution
+            })], 'Changed interaction_solutions.')
+        exploration = exp_fetchers.get_exploration_by_id(self.NEW_EXP_ID)
+        self.assertEqual(
+            exploration.init_state.interaction.solution,
+            None)
 
     def test_cannot_update_recorded_voiceovers_with_invalid_type(self):
         exploration = exp_fetchers.get_exploration_by_id(self.NEW_EXP_ID)
diff --git a/core/domain/state_domain_test.py b/core/domain/state_domain_test.py
--- a/core/domain/state_domain_test.py
+++ b/core/domain/state_domain_test.py
@@ -211,10 +211,14 @@ def test_rte_content_validation_for_android(self):
             },
         }
 
-        init_state.update_interaction_solution(solution_dict)
+        solution = state_domain.Solution.from_dict(
+            init_state.interaction.id, solution_dict
+        )
+        init_state.update_interaction_solution(solution)
         self.assertFalse(init_state.is_rte_content_supported_on_android())
         solution_dict['explanation']['html'] = ''
-        init_state.update_interaction_solution(solution_dict)
+        init_state.update_interaction_solution(state_domain.Solution.from_dict(
+            init_state.interaction.id, solution_dict))
         self.assertTrue(init_state.is_rte_content_supported_on_android())
 
         hints_list = []
@@ -441,7 +445,10 @@ def test_get_content_id_mapping_needing_translations_with_existing_translations(
             },
         }
 
-        init_state.update_interaction_solution(solution_dict)
+        solution = state_domain.Solution.from_dict(
+            init_state.interaction.id, solution_dict)
+        init_state.update_interaction_solution(solution)
+
 
         written_translations_dict = {
             'translations_mapping': {
@@ -869,7 +876,10 @@ def test_hints_validation(self):
             },
         }
 
-        init_state.update_interaction_solution(solution_dict)
+        solution = state_domain.Solution.from_dict(
+            init_state.interaction.id, solution_dict
+        )
+        init_state.update_interaction_solution(solution)
         exploration.validate()
 
         hints_list.append(
@@ -913,7 +923,7 @@ def test_solution_validation(self):
             )
         ]
         init_state.update_interaction_hints(hints_list)
-        solution = {
+        solution_dict = {
             'answer_is_exclusive': False,
             'correct_answer': [0, 0],
             'explanation': {
@@ -926,9 +936,9 @@ def test_solution_validation(self):
         with self.assertRaises(AssertionError):
             init_state.interaction.solution = (
                 state_domain.Solution.from_dict(
-                    init_state.interaction.id, solution))
+                    init_state.interaction.id, solution_dict))
 
-        solution = {
+        solution_dict = {
             'answer_is_exclusive': False,
             'correct_answer': 'hello_world!',
             'explanation': {
@@ -936,7 +946,9 @@ def test_solution_validation(self):
                 'html': '<p>hello_world is a string</p>'
             }
         }
-        init_state.update_interaction_solution(solution)
+        init_state.update_interaction_solution(
+            state_domain.Solution.from_dict(
+                init_state.interaction.id, solution_dict))
         exploration.validate()
 
     def test_validate_state_solicit_answer_details(self):
@@ -974,7 +986,7 @@ def test_validate_solution_answer_is_exclusive(self):
         # Solution should be set to None as default.
         self.assertEqual(exploration.init_state.interaction.solution, None)
 
-        solution = {
+        solution_dict = {
             'answer_is_exclusive': False,
             'correct_answer': 'hello_world!',
             'explanation': {
@@ -987,12 +999,13 @@ def test_validate_solution_answer_is_exclusive(self):
                 state_domain.SubtitledHtml('hint_1', '')
             )
         ]
-
+        solution = state_domain.Solution.from_dict(
+            exploration.init_state.interaction.id, solution_dict)
         exploration.init_state.update_interaction_hints(hints_list)
         exploration.init_state.update_interaction_solution(solution)
         exploration.validate()
 
-        solution = {
+        solution_dict = {
             'answer_is_exclusive': 1,
             'correct_answer': 'hello_world!',
             'explanation': {
@@ -1000,7 +1013,8 @@ def test_validate_solution_answer_is_exclusive(self):
                 'html': '<p>hello_world is a string</p>'
             }
         }
-
+        solution = state_domain.Solution.from_dict(
+            exploration.init_state.interaction.id, solution_dict)
         exploration.init_state.update_interaction_solution(solution)
         with self.assertRaisesRegexp(
             Exception, 'Expected answer_is_exclusive to be bool, received 1'):
@@ -1095,7 +1109,7 @@ def test_validate_duplicate_content_id_with_solution(self):
         hints_list = [state_domain.Hint(subtitled_html)]
 
         exploration.init_state.interaction.hints = hints_list
-        solution = {
+        solution_dict = {
             'answer_is_exclusive': True,
             'correct_answer': 'hello_world!',
             'explanation': {
@@ -1103,7 +1117,8 @@ def test_validate_duplicate_content_id_with_solution(self):
                 'html': '<p>hello_world is a string</p>'
             }
         }
-
+        solution = state_domain.Solution.from_dict(
+            exploration.init_state.interaction.id, solution_dict)
         exploration.init_state.update_interaction_solution(solution)
         exploration.init_state.update_content(
             state_domain.SubtitledHtml.from_dict({
@@ -1319,7 +1334,7 @@ def test_cannot_update_interaction_solution_with_non_dict_solution(self):
                     'hint_1', '<p>Hello, this is html1 for state2</p>')
             )
         ]
-        solution = {
+        solution_dict = {
             'answer_is_exclusive': True,
             'correct_answer': u'hello_world!',
             'explanation': {
@@ -1327,16 +1342,19 @@ def test_cannot_update_interaction_solution_with_non_dict_solution(self):
                 'html': u'<p>hello_world is a string</p>'
             }
         }
-
+        solution = state_domain.Solution.from_dict(
+            exploration.init_state.interaction.id, solution_dict)
         exploration.init_state.update_interaction_hints(hints_list)
         exploration.init_state.update_interaction_solution(solution)
 
         self.assertEqual(
-            exploration.init_state.interaction.solution.to_dict(), solution)
+            exploration.init_state.interaction.solution.to_dict(),
+            solution_dict)
 
         with self.assertRaisesRegexp(
-            Exception, 'Expected solution to be a dict'):
-            exploration.init_state.update_interaction_solution([])
+            Exception, 'Expected solution to be a Solution object,'
+            'recieved test string'):
+            exploration.init_state.update_interaction_solution('test string')
 
     def test_update_interaction_solution_with_no_solution(self):
         exploration = self.save_new_valid_exploration('exp_id', 'owner_id')
diff --git a/core/tests/test_utils.py b/core/tests/test_utils.py
--- a/core/tests/test_utils.py
+++ b/core/tests/test_utils.py
@@ -2202,7 +2202,9 @@ def _create_valid_question_data(self, default_dest_state_name):
                 state_domain.SubtitledHtml('hint_1', '<p>This is a hint.</p>')
             )
         ]
-        state.update_interaction_solution(solution_dict)
+        solution = state_domain.Solution.from_dict(
+            state.interaction.id, solution_dict)
+        state.update_interaction_solution(solution)
         state.update_interaction_hints(hints_list)
         state.interaction.customization_args = {
             'placeholder': 'Enter text here',