1
1
[[chapter_20_fixtures_and_wait_decorator]]
2
- Test Fixtures and a Decorator for [keep-together]#Explicit Waits#
3
- -----------------------------------------------------------------
2
+ == Test Fixtures and a Decorator for [keep-together]#Explicit Waits#
4
3
5
- .Warning, Chapter Not Updated.
4
+ .Warning, Chapter Update in progress
6
5
*******************************************************************************
7
- 🚧 Warning, this Chapter is the 2e version, and uses Django 1.11
6
+ 🚧 Warning, this Chapter is being updates for Django 4 + Python 3.12.
8
7
9
- This chapter and all the following ones are the second edition versions, so they still use Django 1.11, Python 3.8, and so on.
10
-
11
- To follow along with this chapter, it’s probably easiest to reset your code to match my example code as it was in the 2e, by resetting to: https://github.com/hjwp/book-example/tree/chapter_19_mocking
12
-
13
- And you should also probably delete and re-create your virtualenv with * Python 3.8 or 3.9 * and Django 1.11 (pip install "django <2")
14
-
15
- Alternatively, you can muddle through and try and figure out how to make things work with Django 4 etc, but be aware that the listings below won’t be quite right.
8
+ Some code listings may be inconsistent.
16
9
*******************************************************************************
17
10
18
- ((("authentication", "skipping in FTs"))) Now
19
- that we have a functional authentication system, we want to use it to
20
- identify users, and be able to show them all the lists they have created.
11
+ ((("authentication", "skipping in FTs")))
12
+ Now that we have a functional authentication system, we want to use it to identify users,
13
+ and be able to show them all the lists they have created.
21
14
22
- To do that, we're going to have to write FTs that have a logged-in user. Rather
23
- than making each test go through the (time-consuming) login email dance, we
24
- want to be able to skip that part.
15
+ To do that, we're going to have to write FTs that have a logged-in user.
16
+ Rather than making each test go through the (time-consuming) login email dance,
17
+ we want to be able to skip that part.
25
18
26
19
27
-
28
- This is about separation of concerns. Functional tests aren't like unit tests,
29
- in that they don't usually have a single assertion. But, conceptually, they
30
- should be testing a single thing. There's no need for every single FT to test
31
- the login/logout mechanisms. If we can figure out a way to "cheat" and skip
32
- that part, we'll spend less time waiting for duplicated test paths.
20
+ This is about separation of concerns.
21
+ Functional tests aren't like unit tests,
22
+ in that they don't usually have a single assertion.
23
+ But, conceptually, they should be testing a single thing.
24
+ There's no need for every single FT to test the login/logout mechanisms.
25
+ If we can figure out a way to "cheat" and skip that part,
26
+ we'll spend less time waiting for duplicated test paths.
33
27
34
28
TIP: Don't overdo de-duplication in FTs. One of the benefits of an FT is that
35
29
it can catch strange and unpredictable interactions between different
36
30
parts of your application.
37
31
38
32
39
- NOTE: This chapter has only just been rewritten for the new edition, so let me
40
- know via
[email protected] if you spot any problems or have any
41
- suggestions for improvement!
42
-
43
-
44
- Skipping the Login Process by Pre-creating a Session
45
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
46
33
34
+ === Skipping the Login Process by Pre-creating a Session
47
35
48
36
49
- ((("sessions, pre-creating", id="sessions20")))((("login process, skipping", seealso="authentication")))((("cookies"))) It's
50
- quite common for a user to return to a site and still have a cookie, which
51
- means they are "pre-authenticated", so this isn't an unrealistic cheat at all.
37
+ ((("sessions, pre-creating", id="sessions20")))
38
+ ((("login process, skipping", seealso="authentication")))
39
+ ((("cookies")))
40
+ It's quite common for a user to return to a site and still have a cookie,
41
+ which means they are "pre-authenticated",
42
+ so this isn't an unrealistic cheat at all.
52
43
Here's how you can set it up:
53
44
54
45
[role="sourcecode"]
55
- .functional_tests/test_my_lists.py
46
+ .src/ functional_tests/test_my_lists.py
56
47
====
57
48
[source,python]
58
49
----
@@ -138,7 +129,7 @@ in a [keep-together]#`./manage.py`# `shell` if you like:
138
129
[role="skipme small-code"]
139
130
[subs="specialcharacters,macros"]
140
131
----
141
- $ pass:quotes[*python manage.py shell*]
132
+ $ pass:quotes[*python src/ manage.py shell*]
142
133
[...]
143
134
In [1]: from django.contrib.sessions.models import Session
144
135
@@ -172,26 +163,23 @@ to pull them up into `FunctionalTest`. We'll also tweak them slightly so that
172
163
they can take an arbitrary email address as a parameter:
173
164
174
165
[role="sourcecode"]
175
- .functional_tests/base.py (ch18l002 )
166
+ .src/ functional_tests/base.py (ch20l002 )
176
167
====
177
168
[source,python]
178
169
----
179
170
class FunctionalTest(StaticLiveServerTestCase):
180
171
[...]
181
172
182
173
def wait_to_be_logged_in(self, email):
183
- self.wait_for(
184
- lambda: self.browser.find_element_by_link_text('Log out')
185
- )
186
- navbar = self.browser.find_element_by_css_selector('.navbar')
174
+ self.wait_for(lambda: self.browser.find_element(By.LINK_TEXT, "Log out"))
175
+ navbar = self.browser.find_element(By.CSS_SELECTOR, ".navbar")
187
176
self.assertIn(email, navbar.text)
188
177
189
-
190
178
def wait_to_be_logged_out(self, email):
191
179
self.wait_for(
192
- lambda: self.browser.find_element_by_name(' email' )
180
+ lambda: self.browser.find_element(By.CSS_SELECTOR, "input[name= email]" )
193
181
)
194
- navbar = self.browser.find_element_by_css_selector('. navbar' )
182
+ navbar = self.browser.find_element(By.CSS_SELECTOR, ". navbar" )
195
183
self.assertNotIn(email, navbar.text)
196
184
----
197
185
====
@@ -211,7 +199,7 @@ First we use them in 'test_login.py':
211
199
212
200
213
201
[role="sourcecode"]
214
- .functional_tests/test_login.py (ch18l003 )
202
+ .src/ functional_tests/test_login.py (ch20l003 )
215
203
====
216
204
[source,python]
217
205
----
@@ -221,7 +209,7 @@ First we use them in 'test_login.py':
221
209
self.wait_to_be_logged_in(email=TEST_EMAIL)
222
210
223
211
# Now she logs out
224
- self.browser.find_element_by_link_text(' Log out' ).click()
212
+ self.browser.find_element(By.LINK_TEXT, " Log out" ).click()
225
213
226
214
# She is logged out
227
215
self.wait_to_be_logged_out(email=TEST_EMAIL)
@@ -233,7 +221,7 @@ Just to make sure we haven't broken anything, we rerun the login test:
233
221
234
222
[subs="specialcharacters,macros"]
235
223
----
236
- $ pass:quotes[*python manage.py test functional_tests.test_login*]
224
+ $ pass:quotes[*python src/ manage.py test functional_tests.test_login*]
237
225
[...]
238
226
OK
239
227
----
@@ -242,12 +230,12 @@ And now we can write a placeholder for the "My Lists" test, to see if
242
230
our pre-authenticated session creator really does work:
243
231
244
232
[role="sourcecode"]
245
- .functional_tests/test_my_lists.py (ch18l004 )
233
+ .src/ functional_tests/test_my_lists.py (ch20l004 )
246
234
====
247
235
[source,python]
248
236
----
249
237
def test_logged_in_users_lists_are_saved_as_my_lists(self):
250
-
238
+
251
239
self.browser.get(self.live_server_url)
252
240
self.wait_to_be_logged_out(email)
253
241
@@ -262,7 +250,7 @@ That gets us:
262
250
263
251
[subs="specialcharacters,macros"]
264
252
----
265
- $ pass:quotes[*python manage.py test functional_tests.test_my_lists*]
253
+ $ pass:quotes[*python src/ manage.py test functional_tests.test_my_lists*]
266
254
[...]
267
255
OK
268
256
----
@@ -328,28 +316,26 @@ pass:[<code>wait_for_row_​in_list_table</code>] and the inline `self.wait
328
316
329
317
330
318
[role="sourcecode"]
331
- .functional_tests/base.py (ch18l005 )
319
+ .src/ functional_tests/base.py (ch20l005 )
332
320
====
333
321
[source,python]
334
322
----
335
323
@wait
336
324
def wait_for_row_in_list_table(self, row_text):
337
- table = self.browser.find_element_by_id(' id_list_table' )
338
- rows = table.find_elements_by_tag_name('tr' )
325
+ table = self.browser.find_element(By.ID, " id_list_table" )
326
+ rows = table.find_elements(By.TAG_NAME, "tr" )
339
327
self.assertIn(row_text, [row.text for row in rows])
340
328
341
-
342
329
@wait
343
330
def wait_to_be_logged_in(self, email):
344
- self.browser.find_element_by_link_text(' Log out' )
345
- navbar = self.browser.find_element_by_css_selector('. navbar' )
331
+ self.browser.find_element(By.LINK_TEXT, " Log out" )
332
+ navbar = self.browser.find_element(By.CSS_SELECTOR, ". navbar" )
346
333
self.assertIn(email, navbar.text)
347
334
348
-
349
335
@wait
350
336
def wait_to_be_logged_out(self, email):
351
- self.browser.find_element_by_name(' email' )
352
- navbar = self.browser.find_element_by_css_selector('. navbar' )
337
+ self.browser.find_element(By.CSS_SELECTOR, "input[name= email]" )
338
+ navbar = self.browser.find_element(By.CSS_SELECTOR, ". navbar" )
353
339
self.assertNotIn(email, navbar.text)
354
340
----
355
341
====
@@ -371,7 +357,7 @@ timeout occurs. Here's a first cut:
371
357
372
358
373
359
[role="sourcecode"]
374
- .functional_tests/base.py (ch18l006 )
360
+ .src/ functional_tests/base.py (ch20l006 )
375
361
====
376
362
[source,python]
377
363
----
@@ -409,7 +395,7 @@ That's 'almost' right, but not quite; try running it?
409
395
410
396
[subs="specialcharacters,macros"]
411
397
----
412
- $ pass:quotes[*python manage.py test functional_tests.test_my_lists*]
398
+ $ pass:quotes[*python src/ manage.py test functional_tests.test_my_lists*]
413
399
[...]
414
400
self.wait_to_be_logged_out(email)
415
401
TypeError: modified_fn() takes 0 positional arguments but 2 were given
@@ -422,13 +408,13 @@ that have [keep-together]#arguments#:
422
408
423
409
424
410
[role="sourcecode currentcontents"]
425
- .functional_tests/base.py
411
+ .src/ functional_tests/base.py
426
412
====
427
413
[source,python]
428
414
----
429
415
@wait
430
416
def wait_to_be_logged_in(self, email):
431
- self.browser.find_element_by_link_text(' Log out' )
417
+ self.browser.find_element(By.LINK_TEXT, " Log out").click( )
432
418
----
433
419
====
434
420
@@ -445,7 +431,7 @@ arguments"], apparently (I only just learned that):
445
431
446
432
447
433
[role="sourcecode"]
448
- .functional_tests/base.py (ch18l007 )
434
+ .src/ functional_tests/base.py (ch20l007 )
449
435
====
450
436
[source,python]
451
437
----
@@ -476,7 +462,7 @@ is that our decorator now works:
476
462
477
463
[subs="specialcharacters,macros"]
478
464
----
479
- $ pass:quotes[*python manage.py test functional_tests.test_my_lists*]
465
+ $ pass:quotes[*python src/ manage.py test functional_tests.test_my_lists*]
480
466
[...]
481
467
OK
482
468
----
@@ -487,7 +473,7 @@ for our `self.wait_for` helper as well! Like this:
487
473
488
474
489
475
[role="sourcecode"]
490
- .functional_tests/base.py (ch18l008 )
476
+ .src/ functional_tests/base.py (ch20l008 )
491
477
====
492
478
[source,python]
493
479
----
0 commit comments