Skip to content

Commit a22bc77

Browse files
committed
more on working incrementally, some text/whitespace reflowing
1 parent 3001aa3 commit a22bc77

File tree

1 file changed

+88
-86
lines changed

1 file changed

+88
-86
lines changed

chapter_working_incrementally.asciidoc

Lines changed: 88 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,20 @@ Working Incrementally
44

55
((("Test-Driven Development (TDD)", "adapting existing code incrementally", id="TDDadapt07")))
66
((("Testing Goat", "working state to working state")))
7-
Now let's address our real problem, which is that our design only allows for
8-
one global list. In this chapter I'll demonstrate a critical TDD technique:
9-
how to adapt existing code using an incremental, step-by-step process which
10-
takes you from working state to working state. Testing Goat, not Refactoring
11-
Cat.
7+
Now let's address our real problem, which is that our design only allows for one global list.
8+
In this chapter I'll demonstrate a critical TDD technique:
9+
how to adapt existing code using an incremental, step-by-step process
10+
which takes you from working state to working state.
11+
Testing Goat, not Refactoring Cat.
1212

1313

1414
Small Design When Necessary
1515
~~~~~~~~~~~~~~~~~~~~~~~~~~~
1616

1717
((("small vs. big design", id="small07")))
1818
((("multiple lists testing", "small vs. big design", id="MLTsmall07")))
19-
Let's have a think about how we want support for multiple lists to
20-
work. Currently the FT (which is the closest we have to a design document)
21-
says this:
19+
Let's have a think about how we want support for multiple lists to work.
20+
Currently the FT (which is the closest we have to a design document) says this:
2221

2322
[role="sourcecode currentcontents dofirst-ch07l000"]
2423
.functional_tests/tests.py
@@ -36,9 +35,10 @@ says this:
3635
----
3736
====
3837

39-
But really we want to expand on this, by saying that different users
40-
don't see each other's lists, and each get their own URL as a way of
41-
going back to their saved lists. What might a new design look like?
38+
But really we want to expand on this,
39+
by saying that different users don't see each other's lists,
40+
and each get their own URL as a way of going back to their saved lists.
41+
What might a new design look like?
4242

4343

4444

@@ -49,34 +49,33 @@ Not Big Design Up Front
4949
((("Big Design Up Front")))
5050
((("minimum viable applications")))
5151
TDD is closely associated with the agile movement in software development,
52-
which includes a reaction against 'Big Design Up Front', the
53-
traditional software engineering practice whereby, after a lengthy requirements
54-
gathering exercise, there is an equally lengthy design stage where the
55-
software is planned out on paper. The agile philosophy is that you learn more
56-
from solving problems in practice than in theory, especially when you confront
57-
your application with real users as soon as possible.
58-
Instead of a long up-front design phase, we try to put a 'minimum viable
59-
application' out there early, and let the design evolve gradually based on
60-
feedback from real-world usage.
61-
62-
63-
But that doesn't mean that thinking about design is outright banned! In the
64-
last big chapter we saw how just blundering ahead without thinking can
65-
'eventually' get us to the right answer, but often a little thinking about
66-
design can help us get there faster. So, let's think about our minimum viable
67-
lists app, and what kind of design we'll need to deliver it:
52+
which includes a reaction against 'Big Design Up Front',
53+
the traditional software engineering practice whereby,
54+
after a lengthy requirements gathering exercise,
55+
there is an equally lengthy design stage where the software is planned out on paper.
56+
The agile philosophy is that you learn more from solving problems in practice than in theory,
57+
especially when you confront your application with real users as soon as possible.
58+
Instead of a long up-front design phase,
59+
we try to put a 'minimum viable application' out there early,
60+
and let the design evolve gradually based on feedback from real-world usage.
61+
62+
63+
But that doesn't mean that thinking about design is outright banned!
64+
In the last big chapter we saw how just blundering ahead without thinking can 'eventually' get us to the right answer,
65+
but often a little thinking about design can help us get there faster.
66+
So, let's think about our minimum viable lists app,
67+
and what kind of design we'll need to deliver it:
6868

6969
* We want each user to be able to store their own list--at least one, for now.
70-
* A list is made up of several items, whose primary attribute is a bit of
71-
descriptive text.
72-
* We need to save lists from one visit to the next. For now, we can give
73-
each user a unique URL for their list. Later on we may want some way of
74-
automatically recognising users and showing them their lists.
70+
* A list is made up of several items, whose primary attribute is a bit of descriptive text.
71+
* We need to save lists from one visit to the next.
72+
For now, we can give each user a unique URL for their list.
73+
Later on we may want some way of automatically recognising users and showing them their lists.
7574
76-
To deliver the "for now" items, it sounds like we're going to store
77-
lists and their items in a database. Each list will have a unique URL,
78-
and each list item will be a bit of descriptive text, associated with a
79-
particular list.
75+
To deliver the "for now" items,
76+
it sounds like we're going to store lists and their items in a database.
77+
Each list will have a unique URL,
78+
and each list item will be a bit of descriptive text, associated with a particular list.
8079

8180

8281
YAGNI!
@@ -85,19 +84,20 @@ YAGNI!
8584

8685
((("Test-Driven Development (TDD)", "philosophy of", "YAGNI")))
8786
((("YAGNI (You ain’t gonna need it!)")))
88-
Once you start thinking about design, it can be hard to stop. All sorts of
89-
other thoughts are occurring to us--we might want to give each list
90-
a name or title, we might want to recognise users using usernames and
91-
passwords, we might want to add a longer notes field as well as short
92-
descriptions to our list, we might want to store some kind of ordering, and so
93-
on. But we obey another tenet of the agile gospel: "YAGNI" (pronounced
94-
yag-knee), which stands for "You ain't gonna need it!" As software
95-
developers, we have fun creating things, and sometimes it's hard to resist
96-
the urge to build things just because an idea occurred to us and we 'might'
97-
need it. The trouble is that more often than not, no matter how cool the idea
98-
was, you 'won't' end up using it. Instead you have a load of unused code,
99-
adding to the complexity of your application. YAGNI is the mantra we use to
100-
resist our overenthusiastic creative urges.
87+
Once you start thinking about design, it can be hard to stop.
88+
All sorts of other thoughts are occurring to us--we might want to give each list a name or title,
89+
we might want to recognise users using usernames and passwords,
90+
we might want to add a longer notes field as well as short descriptions to our list,
91+
we might want to store some kind of ordering, and so on.
92+
But we obey another tenet of the agile gospel: "YAGNI" (pronounced yag-knee),
93+
which stands for "You ain't gonna need it!"
94+
As software developers, we have fun creating things,
95+
and sometimes it's hard to resist the urge to build things
96+
just because an idea occurred to us and we 'might' need it.
97+
The trouble is that more often than not, no matter how cool the idea was,
98+
you 'won't' end up using it.
99+
Instead you have a load of unused code, adding to the complexity of your application.
100+
YAGNI is the mantra we use to resist our overenthusiastic creative urges.
101101

102102

103103
REST (ish)
@@ -113,7 +113,7 @@ How should the user interact with ++List++s and their ++Item++s using a web brow
113113
Representational State Transfer (REST) is an approach to web design that's
114114
usually used to guide the design of web-based APIs. When designing a
115115
user-facing site, it's not possible to stick 'strictly' to the REST rules,
116-
but they still provide some useful inspiration (skip ahead to
116+
but they still provide some useful inspiration (skip ahead to
117117
<<appendix_rest_api>> if you want to see a real REST API).
118118

119119
REST suggests that we have a URL structure that matches our data structure,
@@ -160,11 +160,10 @@ In summary, our scratchpad for this chapter looks something like this:
160160
*****
161161

162162

163+
163164
Implementing the New Design Incrementally Using TDD
164165
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
165166

166-
167-
168167
((("Test-Driven Development (TDD)", "overall process of")))
169168
((("multiple lists testing", "incremental design implementation")))
170169
How do we use TDD to implement the new design? Let's take another look at
@@ -175,7 +174,7 @@ At the top level, we're going to use a combination of adding new functionality
175174
application--that is, rewriting some of the existing implementation so that it
176175
delivers the same functionality to the user but using aspects of our new
177176
design. We'll be able to use the existing functional test to verify we don't
178-
break what already works, and the new functional test to drive the new
177+
break what already works, and the new functional test to drive the new
179178
features.
180179

181180
At the unit test level, we'll be adding new tests or modifying existing ones to
@@ -214,7 +213,7 @@ def test_can_start_a_list_for_one_user(self):
214213
self.wait_for_row_in_list_table('1: Buy peacock feathers')
215214
216215
# Satisfied, she goes back to sleep
217-
216+
218217
219218
def test_multiple_users_can_start_lists_at_different_urls(self):
220219
# Edith starts a new to-do list
@@ -291,8 +290,8 @@ using the convention of double-hashes (`##`) to indicate
291290
User Story. They're a message to our future selves, which might otherwise
292291
be wondering why the heck we're quitting the browser and starting a new
293292
one...
294-
295-
293+
294+
296295

297296
Other than that, the new test is fairly self-explanatory. Let's see how we do
298297
when we run our FTs:
@@ -661,7 +660,7 @@ Green? Refactor
661660

662661

663662
((("multiple lists testing", "refactoring")))((("refactoring")))((("Red/Green/Refactor")))Time
664-
for a little tidying up.
663+
for a little tidying up.
665664

666665
In the 'Red/Green/Refactor' dance, we've arrived at green, so we should see
667666
what needs a refactor. We now have two views, one for the home page, and one
@@ -685,7 +684,7 @@ class ItemModelTest(TestCase):
685684
686685
----
687686

688-
We can definitely delete the `test_displays_all_list_items` method from
687+
We can definitely delete the `test_displays_all_list_items` method from
689688
`HomePageTest`; it's no longer needed. If you run *`manage.py test lists`*
690689
now, it should say it ran 6 tests instead of 7:
691690

@@ -874,7 +873,7 @@ are we with our own to-do list?
874873

875874
We've 'sort of' made progress on the second item, even if there's still only
876875
one list in the world. The first item is a bit scary. Can we do something
877-
about items 3 or 4?
876+
about items 3 or 4?
878877

879878
Let's have a new URL for adding new list items. If nothing else, it'll
880879
simplify the home page view.
@@ -942,7 +941,7 @@ code was 404 (expected 302)
942941
----
943942

944943
The first failure tells us we're not saving a new item to the database, and the
945-
second says that, instead of returning a 302 redirect, our view is returning
944+
second says that, instead of returning a 302 redirect, our view is returning
946945
a 404. That's because we haven't built a URL for '/lists/new', so the
947946
`client.post` is just getting a "not found" response.
948947

@@ -1091,19 +1090,22 @@ Oops:
10911090
----
10921091
ERROR: test_can_start_a_list_for_one_user
10931092
[...]
1094-
File "...python-tdd-book/functional_tests/tests.py", line 57, in
1093+
File "...python-tdd-book/functional_tests/tests.py", line 58, in
10951094
test_can_start_a_list_for_one_user
10961095
self.wait_for_row_in_list_table('1: Buy peacock feathers')
1097-
File "...python-tdd-book/functional_tests/tests.py", line 23, in
1096+
File "...python-tdd-book/functional_tests/tests.py", line 24, in
10981097
wait_for_row_in_list_table
10991098
table = self.browser.find_element(By.ID, 'id_list_table')
1099+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1100+
[...]
11001101
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate
11011102
element: [id="id_list_table"]
11021103
1104+
11031105
FAIL: test_multiple_users_can_start_lists_at_different_urls (functional_tests.t
11041106
ests.NewVisitorTest.test_multiple_users_can_start_lists_at_different_urls)
11051107
[...]
1106-
File "...python-tdd-book/functional_tests/tests.py", line 79, in
1108+
File "...python-tdd-book/functional_tests/tests.py", line 80, in
11071109
test_multiple_users_can_start_lists_at_different_urls
11081110
self.wait_for_row_in_list_table('1: Buy peacock feathers')
11091111
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate
@@ -1184,16 +1186,16 @@ present the changes in the form of a diff:
11841186
from django.test import TestCase
11851187
-from lists.models import Item
11861188
+from lists.models import Item, List
1187-
1188-
1189+
1190+
11891191
class HomePageTest(TestCase):
11901192
@@ -44,22 +44,32 @@ class ListViewTest(TestCase):
1191-
1192-
1193-
1193+
1194+
1195+
11941196
-class ItemModelTest(TestCase):
11951197
+class ListAndItemModelsTest(TestCase):
1196-
1198+
11971199
def test_saving_and_retrieving_items(self):
11981200
+ list_ = List()
11991201
+ list_.save()
@@ -1202,18 +1204,18 @@ present the changes in the form of a diff:
12021204
first_item.text = 'The first (ever) list item'
12031205
+ first_item.list = list_
12041206
first_item.save()
1205-
1207+
12061208
second_item = Item()
12071209
second_item.text = 'Item the second'
12081210
+ second_item.list = list_
12091211
second_item.save()
1210-
1212+
12111213
+ saved_list = List.objects.first()
12121214
+ self.assertEqual(saved_list, list_)
12131215
+
12141216
saved_items = Item.objects.all()
12151217
self.assertEqual(saved_items.count(), 2)
1216-
1218+
12171219
first_saved_item = saved_items[0]
12181220
second_saved_item = saved_items[1]
12191221
self.assertEqual(first_saved_item.text, 'The first (ever) list item')
@@ -1236,7 +1238,7 @@ NOTE: I'm using the variable name `list_` to avoid "shadowing" the Python
12361238
built-in `list` function. It's ugly, but all the other options I tried
12371239
were equally ugly or worse (`my_list`, `the_list`, `list1`, `listey`...).
12381240

1239-
Time for another unit-test/code cycle.
1241+
Time for another unit-test/code cycle.
12401242

12411243
For the first couple of iterations, rather than explicitly showing you what
12421244
code to enter in between every test run, I'm only going to show you the
@@ -1379,7 +1381,7 @@ WARNING: Deleting migrations is dangerous. We do need to do it now and again,
13791381
Django will be confused about what state it's in, and how to apply future
13801382
migrations. You should only do it when you're sure the migration hasn't
13811383
been used. A good rule of thumb is that you should never delete or modify
1382-
a migration that's already been committed to your VCS.
1384+
a migration that's already been committed to your VCS.
13831385

13841386

13851387

@@ -1406,13 +1408,13 @@ Ran 6 tests in 0.021s
14061408
FAILED (errors=3)
14071409
----
14081410

1409-
Oh dear!
1411+
Oh dear!
14101412

14111413
There is some good news. Although it's hard to see, our model tests are
14121414
passing. But three of our view tests are failing nastily.
14131415

14141416
The reason is because of the new relationship we've introduced between
1415-
++Item++s and ++List++s, which requires each item to have a parent list, which
1417+
++Item++s and ++List++s, which requires each item to have a parent list, which
14161418
our old tests and code aren't prepared for.
14171419

14181420
Still, this is exactly why we have tests! Let's get them working again. The
@@ -1563,11 +1565,11 @@ class ListViewTest(TestCase):
15631565

15641566

15651567
NOTE: A couple more of those lovely f-strings in this listing! If they're
1566-
still a bit of a mystery, take a look at the
1568+
still a bit of a mystery, take a look at the
15671569
https://docs.python.org/3/reference/lexical_analysis.html#f-strings[docs]
15681570
(although if your formal CS education is as bad as mine, you'll probably
15691571
skip the formal grammar).
1570-
1572+
15711573

15721574
Running the unit tests gives an expected 404, and another related error:
15731575

@@ -1760,7 +1762,7 @@ FAIL: test_can_start_a_list_for_one_user
17601762
(functional_tests.tests.NewVisitorTest)
17611763
---------------------------------------------------------------------
17621764
Traceback (most recent call last):
1763-
File "...python-tdd-book/functional_tests/tests.py", line 67, in
1765+
File "...python-tdd-book/functional_tests/tests.py", line 68, in
17641766
test_can_start_a_list_for_one_user
17651767
self.wait_for_row_in_list_table('2: Use peacock feathers to make a fly')
17661768
[...]
@@ -2139,7 +2141,7 @@ But we can fix it in 'list.html', as well as adjusting the form's POST action:
21392141
<1> There's our new form action.
21402142

21412143
<2> `.item_set` is ((("reverse lookups")))called
2142-
a
2144+
a
21432145
https://docs.djangoproject.com/en/1.11/topics/db/queries/#following-relationships-backward[reverse lookup].
21442146
It's one of Django's incredibly useful bits of ORM that lets you look up an
21452147
object's related items from a different table...
@@ -2256,7 +2258,7 @@ urlpatterns = [
22562258
----
22572259
====
22582260

2259-
Rerun the unit tests to check that everything worked.
2261+
Rerun the unit tests to check that everything worked.
22602262

22612263

22622264
When I did it, I couldn't quite believe I did it correctly on the first go. It
@@ -2298,9 +2300,9 @@ natural urge is often to dive in and fix everything at once...but if
22982300
loads of changes to our code and nothing working. The Testing Goat
22992301
encourages us to take one step at a time, and go from working state to
23002302
working state.
2301-
2302-
2303-
2303+
2304+
2305+
23042306
23052307
23062308
Split work out into small, achievable tasks::
@@ -2309,11 +2311,11 @@ this means starting with "boring" work rather than diving
23092311
straight in with the fun stuff, but you'll have to trust that YOLO-you
23102312
in the parallel universe is probably having a bad time, having broken
23112313
everything, and struggling to get the app working again.
2312-
2314+
23132315
23142316
YAGNI::
23152317
((("Test-Driven Development (TDD)", "philosophy of", "YAGNI")))((("YAGNI (You ain&#x2019;t gonna need it!)")))You
2316-
ain't gonna need it! Avoid the temptation to write code that you
2318+
ain't gonna need it! Avoid the temptation to write code that you
23172319
think 'might' be useful, just because it suggests itself at the time.
23182320
Chances are, you won't use it, or you won't have anticipated your
23192321
future requirements correctly. See <<chapter_outside_in>> for one

0 commit comments

Comments
 (0)