If we have a look through our form tests, we’ll see that, actually, only item
➌
is tested
explicitly. On items
➊
and
➋
we were lucky—they’re default features of a Django
ModelForm
, and they are actually covered by our tests for the parent
ItemForm
class.
But contract clause number
➍
managed to slip through the net.
When doing outside-in TDD with isolated tests, you need to keep
track of each test’s implicit assumptions about the contract which the
next layer should implement, and remember to test each of those in
turn later. You could use our scratchpad for this, or create a place‐
holder test with a
self.fail
.
Fixing the Oversight
Let’s add a new test that our form should return the new saved list:
lists/tests/test_forms.py (ch19l038-1).
@patch
(
'lists.forms.List.create_new'
)
def
test_save_returns_new_list_object
(
self
,
mock_List_create_new
):
user
=
Mock
(
is_authenticated
=
lambda
:
True
)
form
=
NewListForm
(
data
=
{
'text'
:
'new item text'
})
form
.
is_valid
()
response
=
form
.
save
(
owner
=
user
)
self
.
assertEqual
(
response
,
mock_List_create_new
.
return_value
)
And, actually, this is a good example—we have an implicit contract with the
List.cre
ate_new
, we want it to return the new list object. Let’s add a placeholder test for that.
lists/tests/test_models.py (ch19l038-2).
class
ListModelTest
(
TestCase
):
[
...
]
def
test_create_returns_new_list_object
(
self
):
self
.
fail
()
So, we have one test failures that’s telling us to fix the form save:
AssertionError: None != <MagicMock name='create_new()' id='139802647565536'>
FAILED (failures=2, errors=3)
Like this:
lists/forms.py (ch19l039-1).
class
NewListForm
(
ItemForm
):
def
save
(
self
,
owner
):
if
owner
.
is_authenticated
():
return
List
.
create_new
(
first_item_text
=
self
.
cleaned_data
[
'text'
],
owner
=
owner
)
else
:
return
List
.
create_new
(
first_item_text
=
self
.
cleaned_data
[
'text'
])
That’s a start, now we should look at our placeholder test:
Thinking of Interactions Between Layers as “Contracts”
|
357