1. It could easily just be a standalone function, but hanging it on the model class is a nice way to keep track of
where it lives, and gives a bit more of a hint as to what it will do.
mock_item
=
mockItem
.
return_value
mock_list
=
mockList
.
return_value
user
=
Mock
()
form
=
NewListForm
(
data
=
{
'text'
:
'new item text'
})
form
.
is_valid
()
#
def
check_item_text_and_list
():
self
.
assertEqual
(
mock_item
.
text
,
'new item text'
)
self
.
assertEqual
(
mock_item
.
list
,
mock_list
)
self
.
assertTrue
(
mock_list
.
save
.
called
)
mock_item
.
save
.
side_effect
=
check_item_text_and_list
#
form
.
save
(
owner
=
user
)
self
.
assertTrue
(
mock_item
.
save
.
called
)
#
We mock out the two collaborators for our form from the models layer below.
We need to call
is_valid()
so that the form populates the
.cleaned_data
dictionary where it stores validated data.
We use the
side_effect
method to make sure that, when we save the new item
object, we’re doing so with a saved List and with the correct item text.
As always, we double-check that our side-effect function was actually called.
Yuck! What an ugly test!
Keep Listening to Your Tests: Removing ORM Code from Our
Application
Again, these tests are trying to tell us something: the Django ORM is hard to mock out,
and our form class needs to know too much about how it works. Programming by
wishful thinking again, what would be a simpler API that our form could use? How
about something like this:
def
save
(
self
):
List
.
create_new
(
first_item_text
=
self
.
cleaned_data
[
'text'
])
Our wishful thinking says: how about we had a helper method that would live on the
List class
1
and it will encapsulate all the logic of saving a new list object and its associated
first item.
So let’s write a test for that instead:
348
|
Chapter 19: Test Isolation, and “Listening to Your Tests”