@@ -73,20 +73,20 @@ marked ``smtp`` fixture function. Running the test looks like this::
73
73
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
74
74
rootdir: $REGENDOC_TMPDIR, inifile:
75
75
collected 1 items
76
-
76
+
77
77
test_smtpsimple.py F
78
-
78
+
79
79
======= FAILURES ========
80
80
_______ test_ehlo ________
81
-
81
+
82
82
smtp = <smtplib.SMTP object at 0xdeadbeef>
83
-
83
+
84
84
def test_ehlo(smtp):
85
85
response, msg = smtp.ehlo()
86
86
assert response == 250
87
87
> assert 0 # for demo purposes
88
88
E assert 0
89
-
89
+
90
90
test_smtpsimple.py:11: AssertionError
91
91
======= 1 failed in 0.12 seconds ========
92
92
@@ -123,20 +123,13 @@ with a list of available function arguments.
123
123
but is not anymore advertised as the primary means of declaring fixture
124
124
functions.
125
125
126
- "Funcargs" a prime example of dependency injection
126
+ Fixtures: a prime example of dependency injection
127
127
---------------------------------------------------
128
128
129
- When injecting fixtures to test functions, pytest-2.0 introduced the
130
- term "funcargs" or "funcarg mechanism" which continues to be present
131
- also in docs today. It now refers to the specific case of injecting
132
- fixture values as arguments to test functions. With pytest-2.3 there are
133
- more possibilities to use fixtures but "funcargs" remain as the main way
134
- as they allow to directly state the dependencies of a test function.
135
-
136
- As the following examples show in more detail, funcargs allow test
137
- functions to easily receive and work against specific pre-initialized
138
- application objects without having to care about import/setup/cleanup
139
- details. It's a prime example of `dependency injection `_ where fixture
129
+ Fixtures allow test functions to easily receive and work
130
+ against specific pre-initialized application objects without having
131
+ to care about import/setup/cleanup details.
132
+ It's a prime example of `dependency injection `_ where fixture
140
133
functions take the role of the *injector * and test functions are the
141
134
*consumers * of fixture objects.
142
135
@@ -176,7 +169,7 @@ function (in or below the directory where ``conftest.py`` is located)::
176
169
response, msg = smtp.ehlo()
177
170
assert response == 250
178
171
assert b"smtp.gmail.com" in msg
179
- assert 0 # for demo purposes
172
+ assert 0 # for demo purposes
180
173
181
174
def test_noop(smtp):
182
175
response, msg = smtp.noop()
@@ -191,32 +184,32 @@ inspect what is going on and can now run the tests::
191
184
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
192
185
rootdir: $REGENDOC_TMPDIR, inifile:
193
186
collected 2 items
194
-
187
+
195
188
test_module.py FF
196
-
189
+
197
190
======= FAILURES ========
198
191
_______ test_ehlo ________
199
-
192
+
200
193
smtp = <smtplib.SMTP object at 0xdeadbeef>
201
-
194
+
202
195
def test_ehlo(smtp):
203
196
response, msg = smtp.ehlo()
204
197
assert response == 250
205
198
assert b"smtp.gmail.com" in msg
206
199
> assert 0 # for demo purposes
207
200
E assert 0
208
-
201
+
209
202
test_module.py:6: AssertionError
210
203
_______ test_noop ________
211
-
204
+
212
205
smtp = <smtplib.SMTP object at 0xdeadbeef>
213
-
206
+
214
207
def test_noop(smtp):
215
208
response, msg = smtp.noop()
216
209
assert response == 250
217
210
> assert 0 # for demo purposes
218
211
E assert 0
219
-
212
+
220
213
test_module.py:11: AssertionError
221
214
======= 2 failed in 0.12 seconds ========
222
215
@@ -267,7 +260,7 @@ Let's execute it::
267
260
268
261
$ pytest -s -q --tb=no
269
262
FFteardown smtp
270
-
263
+
271
264
2 failed in 0.12 seconds
272
265
273
266
We see that the ``smtp `` instance is finalized after the two
@@ -296,36 +289,61 @@ The ``smtp`` connection will be closed after the test finished execution
296
289
because the ``smtp `` object automatically closes when
297
290
the ``with `` statement ends.
298
291
292
+ Note that if an exception happens during the *setup * code (before the ``yield `` keyword), the
293
+ *teardown * code (after the ``yield ``) will not be called.
294
+
299
295
300
296
.. note ::
301
297
Prior to version 2.10, in order to use a ``yield `` statement to execute teardown code one
302
298
had to mark a fixture using the ``yield_fixture `` marker. From 2.10 onward, normal
303
299
fixtures can use ``yield `` directly so the ``yield_fixture `` decorator is no longer needed
304
300
and considered deprecated.
305
301
306
- .. note ::
307
- As historical note, another way to write teardown code is
308
- by accepting a ``request `` object into your fixture function and can call its
309
- ``request.addfinalizer `` one or multiple times::
310
302
311
- # content of conftest.py
303
+ An alternative option for executing *teardown * code is to
304
+ make use of the ``addfinalizer `` method of the `request-context `_ object to register
305
+ finalization functions.
312
306
313
- import smtplib
314
- import pytest
307
+ Here's the ``smtp `` fixture changed to use ``addfinalizer `` for cleanup:
308
+
309
+ .. code-block :: python
310
+
311
+ # content of conftest.py
312
+ import smtplib
313
+ import pytest
314
+
315
+ @pytest.fixture (scope = " module" )
316
+ def smtp (request ):
317
+ smtp = smtplib.SMTP(" smtp.gmail.com" )
318
+ def fin ():
319
+ print (" teardown smtp" )
320
+ smtp.close()
321
+ request.addfinalizer(fin)
322
+ return smtp # provide the fixture value
315
323
316
- @pytest.fixture(scope="module")
317
- def smtp(request):
318
- smtp = smtplib.SMTP("smtp.gmail.com")
319
- def fin():
320
- print ("teardown smtp")
321
- smtp.close()
322
- request.addfinalizer(fin)
323
- return smtp # provide the fixture value
324
324
325
- The ``fin `` function will execute when the last test in the module has finished execution.
325
+ Both ``yield `` and ``addfinalizer `` methods work similarly by calling their code after the test
326
+ ends, but ``addfinalizer `` has two key differences over ``yield ``:
327
+
328
+ 1. It is possible to register multiple finalizer functions.
329
+
330
+ 2. Finalizers will always be called regardless if the fixture *setup * code raises an exception.
331
+ This is handy to properly close all resources created by a fixture even if one of them
332
+ fails to be created/acquired::
333
+
334
+ @pytest.fixture
335
+ def equipments(request):
336
+ r = []
337
+ for port in ('C1', 'C3', 'C28'):
338
+ equip = connect(port)
339
+ request.addfinalizer(equip.disconnect)
340
+ r.append(equip)
341
+ return r
342
+
343
+ In the example above, if ``"C28" `` fails with an exception, ``"C1" `` and ``"C3" `` will still
344
+ be properly closed. Of course, if an exception happens before the finalize function is
345
+ registered then it will not be executed.
326
346
327
- This method is still fully supported, but ``yield `` is recommended from 2.10 onward because
328
- it is considered simpler and better describes the natural code flow.
329
347
330
348
.. _`request-context` :
331
349
@@ -355,7 +373,7 @@ again, nothing much has changed::
355
373
356
374
$ pytest -s -q --tb=no
357
375
FFfinalizing <smtplib.SMTP object at 0xdeadbeef> (smtp.gmail.com)
358
-
376
+
359
377
2 failed in 0.12 seconds
360
378
361
379
Let's quickly create another test module that actually sets the
@@ -423,51 +441,51 @@ So let's just do another run::
423
441
FFFF
424
442
======= FAILURES ========
425
443
_______ test_ehlo[smtp.gmail.com] ________
426
-
444
+
427
445
smtp = <smtplib.SMTP object at 0xdeadbeef>
428
-
446
+
429
447
def test_ehlo(smtp):
430
448
response, msg = smtp.ehlo()
431
449
assert response == 250
432
450
assert b"smtp.gmail.com" in msg
433
451
> assert 0 # for demo purposes
434
452
E assert 0
435
-
453
+
436
454
test_module.py:6: AssertionError
437
455
_______ test_noop[smtp.gmail.com] ________
438
-
456
+
439
457
smtp = <smtplib.SMTP object at 0xdeadbeef>
440
-
458
+
441
459
def test_noop(smtp):
442
460
response, msg = smtp.noop()
443
461
assert response == 250
444
462
> assert 0 # for demo purposes
445
463
E assert 0
446
-
464
+
447
465
test_module.py:11: AssertionError
448
466
_______ test_ehlo[mail.python.org] ________
449
-
467
+
450
468
smtp = <smtplib.SMTP object at 0xdeadbeef>
451
-
469
+
452
470
def test_ehlo(smtp):
453
471
response, msg = smtp.ehlo()
454
472
assert response == 250
455
473
> assert b"smtp.gmail.com" in msg
456
474
E AssertionError: assert b'smtp.gmail.com' in b'mail.python.org\nSIZE 51200000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8'
457
-
475
+
458
476
test_module.py:5: AssertionError
459
477
-------------------------- Captured stdout setup ---------------------------
460
478
finalizing <smtplib.SMTP object at 0xdeadbeef>
461
479
_______ test_noop[mail.python.org] ________
462
-
480
+
463
481
smtp = <smtplib.SMTP object at 0xdeadbeef>
464
-
482
+
465
483
def test_noop(smtp):
466
484
response, msg = smtp.noop()
467
485
assert response == 250
468
486
> assert 0 # for demo purposes
469
487
E assert 0
470
-
488
+
471
489
test_module.py:11: AssertionError
472
490
------------------------- Captured stdout teardown -------------------------
473
491
finalizing <smtplib.SMTP object at 0xdeadbeef>
@@ -539,7 +557,7 @@ Running the above tests results in the following test IDs being used::
539
557
<Function 'test_noop[smtp.gmail.com]'>
540
558
<Function 'test_ehlo[mail.python.org]'>
541
559
<Function 'test_noop[mail.python.org]'>
542
-
560
+
543
561
======= no tests ran in 0.12 seconds ========
544
562
545
563
.. _`interdependent fixtures` :
@@ -578,10 +596,10 @@ Here we declare an ``app`` fixture which receives the previously defined
578
596
cachedir: .cache
579
597
rootdir: $REGENDOC_TMPDIR, inifile:
580
598
collecting ... collected 2 items
581
-
599
+
582
600
test_appsetup.py::test_smtp_exists[smtp.gmail.com] PASSED
583
601
test_appsetup.py::test_smtp_exists[mail.python.org] PASSED
584
-
602
+
585
603
======= 2 passed in 0.12 seconds ========
586
604
587
605
Due to the parametrization of ``smtp `` the test will run twice with two
@@ -647,40 +665,40 @@ Let's run the tests in verbose mode and with looking at the print-output::
647
665
cachedir: .cache
648
666
rootdir: $REGENDOC_TMPDIR, inifile:
649
667
collecting ... collected 8 items
650
-
668
+
651
669
test_module.py::test_0[1] SETUP otherarg 1
652
670
RUN test0 with otherarg 1
653
671
PASSED TEARDOWN otherarg 1
654
-
672
+
655
673
test_module.py::test_0[2] SETUP otherarg 2
656
674
RUN test0 with otherarg 2
657
675
PASSED TEARDOWN otherarg 2
658
-
676
+
659
677
test_module.py::test_1[mod1] SETUP modarg mod1
660
678
RUN test1 with modarg mod1
661
679
PASSED
662
680
test_module.py::test_2[1-mod1] SETUP otherarg 1
663
681
RUN test2 with otherarg 1 and modarg mod1
664
682
PASSED TEARDOWN otherarg 1
665
-
683
+
666
684
test_module.py::test_2[2-mod1] SETUP otherarg 2
667
685
RUN test2 with otherarg 2 and modarg mod1
668
686
PASSED TEARDOWN otherarg 2
669
-
687
+
670
688
test_module.py::test_1[mod2] TEARDOWN modarg mod1
671
689
SETUP modarg mod2
672
690
RUN test1 with modarg mod2
673
691
PASSED
674
692
test_module.py::test_2[1-mod2] SETUP otherarg 1
675
693
RUN test2 with otherarg 1 and modarg mod2
676
694
PASSED TEARDOWN otherarg 1
677
-
695
+
678
696
test_module.py::test_2[2-mod2] SETUP otherarg 2
679
697
RUN test2 with otherarg 2 and modarg mod2
680
698
PASSED TEARDOWN otherarg 2
681
699
TEARDOWN modarg mod2
682
-
683
-
700
+
701
+
684
702
======= 8 passed in 0.12 seconds ========
685
703
686
704
You can see that the parametrized module-scoped ``modarg `` resource caused an
@@ -782,8 +800,8 @@ Autouse fixtures (xUnit setup on steroids)
782
800
.. regendoc:wipe
783
801
784
802
Occasionally, you may want to have fixtures get invoked automatically
785
- without a ` usefixtures `_ or ` funcargs `_ reference. As a practical
786
- example, suppose we have a database fixture which has a
803
+ without declaring a function argument explicitly or a ` usefixtures `_ decorator.
804
+ As a practical example, suppose we have a database fixture which has a
787
805
begin/rollback/commit architecture and we want to automatically surround
788
806
each test method by a transaction and a rollback. Here is a dummy
789
807
self-contained implementation of this idea::
0 commit comments