@@ -27,7 +27,7 @@ functionality in tests:
27
27
All modifications will be undone after the requesting
28
28
test function or fixture has finished. The ``raising ``
29
29
parameter determines if a ``KeyError `` or ``AttributeError ``
30
- will be raised if the set/deletion operation has no target .
30
+ will be raised if the target of the set/deletion operation does not exist .
31
31
32
32
Consider the following scenarios:
33
33
@@ -46,7 +46,7 @@ environment variable is missing, or to set multiple values to a known variable.
46
46
:py:meth: `monkeypatch.setenv ` and :py:meth: `monkeypatch.delenv ` can be used for
47
47
these patches.
48
48
49
- 4. Use :py:meth: `monkeypatch.syspath_prepend ` to add to the system ``$PATH `` and
49
+ 4. Use :py:meth: `monkeypatch.syspath_prepend ` to modify the system ``$PATH `` and
50
50
:py:meth: `monkeypatch.chdir ` to change the context of the current working directory
51
51
during a test.
52
52
@@ -59,33 +59,32 @@ Simple example: monkeypatching functions
59
59
----------------------------------------
60
60
61
61
Consider a scenario where you are working with user directories. In the context of
62
- testing, you do not want your test to depend on the running user. ``Monkeypatch ``
62
+ testing, you do not want your test to depend on the running user. ``monkeypatch ``
63
63
can be used to patch functions dependent on the user to always return a
64
64
specific value.
65
65
66
66
In this example, :py:meth: `monkeypatch.setattr ` is used to patch ``os.path.expanduser ``
67
67
so that the known testing string ``"/abc" `` is always used when the test is run.
68
- This removes any dependency on the `` $USER `` environment variable for testing purposes.
68
+ This removes any dependency on the running user for testing purposes.
69
69
:py:meth: `monkeypatch.setattr ` must be called before the function which will use
70
70
the patched function is called.
71
71
After the test function finishes the ``os.path.expanduser `` modification will be undone.
72
72
73
73
.. code-block :: python
74
74
75
- # Content of test_module.py with source code and the test.
76
- # os.path is imported for reference in monkeypatch.setattr().
75
+ # Contents of test_module.py with source code and the test
76
+ # os.path is imported for reference in monkeypatch.setattr()
77
77
import os.path
78
78
79
79
80
80
def getssh ():
81
81
""" Simple function to return expanded homedir ssh path from $USER"""
82
- user = os.getenv(" $USER" )
83
- return os.path.join(os.path.expanduser(f " ~ { user} " ), " .ssh" )
82
+ return os.path.expanduser(" ~/.ssh" )
84
83
85
84
86
85
def test_getssh (monkeypatch ):
87
- # The mocked return function to replace os.path.expanduser.
88
- # Given a path, always return '/abc'.
86
+ # The mocked return function to replace os.path.expanduser
87
+ # Given a path, always return '/abc'
89
88
def mockreturn (path ):
90
89
return " /abc"
91
90
@@ -108,7 +107,7 @@ Imagine a simple function to take an API url and return the json response.
108
107
109
108
.. code-block :: python
110
109
111
- # Contents of app.py, a simple API retrieval example.
110
+ # Contents of app.py, a simple API retrieval example
112
111
import requests
113
112
114
113
@@ -123,44 +122,44 @@ This can be done in our test file by defining a class to represent ``r``.
123
122
124
123
.. code-block :: python
125
124
126
- # Contents of test_app.py, a simple test for our API retrieval.
127
- # Import requests for the purposes of monkeypatching.
125
+ # Contents of test_app.py, a simple test for our API retrieval
126
+ # Import requests for the purposes of monkeypatching
128
127
import requests
129
128
130
- # Our app.py that includes the get_json() function.
131
- # This is the previous code block example.
129
+ # Our app.py that includes the get_json() function
130
+ # This is the previous code block example
132
131
import app
133
132
134
- # A Custom class to be the mock return value.
135
- # It needs to override the requests.Response returned from requests.get.
133
+ # A Custom class to be the mock return value
134
+ # It needs to override the requests.Response returned from requests.get
136
135
class MockResponse :
137
136
138
- # The class has a json() method that we want to mock.
139
- # The mock method always returns a specific testing dictionary.
137
+ # The class has a json() method that we want to mock
138
+ # The mock method always returns a specific testing dictionary
140
139
@ staticmethod
141
140
def json ():
142
141
return {" mock_key" : " mock_response" }
143
142
144
143
145
144
def test_get_json (monkeypatch ):
146
145
147
- # This function is used in the monkeypatch of requests.get().
146
+ # This function is used in the monkeypatch of requests.get()
148
147
# Any arguments may be passed and it will always return our
149
148
# mocked object, which only has the .json() method.
150
149
def mock_get (* args , ** kwargs ):
151
150
return MockResponse()
152
151
153
- # Apply the monkeypatch for requests.get to mock_get.
152
+ # Apply the monkeypatch for requests.get to mock_get
154
153
monkeypatch.setattr(requests, " get" , mock_get)
155
154
156
- # Our simple but illustrative test.
157
- # app.get_json, which contains requests.get, uses the monkeypatch.
158
- # Therefore, our result is our mock.
155
+ # Our simple but illustrative test
156
+ # app.get_json, which contains requests.get, uses the monkeypatch
157
+ # Therefore, our result is our mock
159
158
result = app.get_json(" https://fakeurl" )
160
159
assert result[" mock_key" ] == " mock_response"
161
160
162
161
163
- ``Monkeypatch `` applies the mock for ``requests.get `` with our ``mock_get `` function.
162
+ ``monkeypatch `` applies the mock for ``requests.get `` with our ``mock_get `` function.
164
163
The ``mock_get `` function returns an instance of the ``MockResponse `` class, which
165
164
has a ``json() `` method defined to return a known testing dictionary and does not
166
165
require any outside API connection.
@@ -170,25 +169,25 @@ the scenario you are testing. For instance, it could include an ``ok`` property
170
169
always returns ``True ``, or return different values from the ``json() `` mocked method
171
170
based on input strings.
172
171
173
- This mock can be shared across tests using the ``fixture `` syntax :
172
+ This mock can be shared across tests using a ``fixture ``:
174
173
175
174
.. code-block :: python
176
175
177
- # Contents of test_app.py, a simple test for our API retrieval.
176
+ # Contents of test_app.py, a simple test for our API retrieval
178
177
import pytest
179
178
import requests
180
179
181
- # Our app.py that includes the get_json() function.
180
+ # Our app.py that includes the get_json() function
182
181
import app
183
182
184
- # A Custom class to be the mock.
183
+ # A Custom class to be the mock
185
184
class MockResponse :
186
185
@ staticmethod
187
186
def json ():
188
187
return {" mock_key" : " mock_response" }
189
188
190
189
191
- # Monkeypatched requests.get moved to a fixture.
190
+ # monkeypatched requests.get moved to a fixture
192
191
@pytest.fixture
193
192
def mock_response (monkeypatch ):
194
193
""" Requests.get() mocked to return {'mock_key':'mock_response'}."""
@@ -199,14 +198,14 @@ This mock can be shared across tests using the ``fixture`` syntax:
199
198
monkeypatch.setattr(requests, " get" , mock_get)
200
199
201
200
202
- # Notice our test uses the custom fixture instead of monkeypatch directly.
201
+ # Notice our test uses the custom fixture instead of monkeypatch directly
203
202
def test_get_json (mock_response ):
204
203
result = app.get_json(" https://fakeurl" )
205
204
assert result[" mock_key" ] == " mock_response"
206
205
207
206
208
207
Furthermore, if the mock was designed to be applied to all tests, the ``fixture `` could
209
- be moved to ``conftest.py `` with ``autouse=True ``.
208
+ be moved to a ``conftest.py `` file and use the with ``autouse=True `` option .
210
209
211
210
212
211
Global patch example: preventing "requests" from remote operations
@@ -261,12 +260,12 @@ Monkeypatching environment variables
261
260
------------------------------------
262
261
263
262
If you are working with environment variables you often need to safely change the values
264
- or delete them from the system for testing purposes. ``Monkeypatch `` provides a mechanism
263
+ or delete them from the system for testing purposes. ``monkeypatch `` provides a mechanism
265
264
to do this using the ``setenv `` and ``delenv `` method. Our example code to test:
266
265
267
266
.. code-block :: python
268
267
269
- # Contents of our original code file e.g. code.py.
268
+ # Contents of our original code file e.g. code.py
270
269
import os
271
270
272
271
@@ -286,7 +285,7 @@ both paths can be safely tested without impacting the running environment:
286
285
287
286
.. code-block :: python
288
287
289
- # Contents of our test file e.g. test_code.py.
288
+ # Contents of our test file e.g. test_code.py
290
289
import pytest
291
290
292
291
@@ -307,6 +306,7 @@ This behavior can be moved into ``fixture`` structures and shared across tests:
307
306
308
307
.. code-block :: python
309
308
309
+ # Contents of our test file e.g. test_code.py
310
310
import pytest
311
311
312
312
@@ -320,7 +320,7 @@ This behavior can be moved into ``fixture`` structures and shared across tests:
320
320
monkeypatch.delenv(" USER" , raising = False )
321
321
322
322
323
- # Notice the tests reference the fixtures for mocks.
323
+ # Notice the tests reference the fixtures for mocks
324
324
def test_upper_to_lower (mock_env_user ):
325
325
assert get_os_user_lower() == " testinguser"
326
326
@@ -338,7 +338,7 @@ to specific values during tests. Take this simplified connection string example:
338
338
339
339
.. code-block :: python
340
340
341
- # Contents of app.py to generate a simple connection string.
341
+ # Contents of app.py to generate a simple connection string
342
342
DEFAULT_CONFIG = {" user" : " user1" , " database" : " db1" }
343
343
344
344
@@ -351,8 +351,8 @@ For testing purposes we can patch the ``DEFAULT_CONFIG`` dictionary to specific
351
351
352
352
.. code-block :: python
353
353
354
- # Contents of test_app.py.
355
- # Our app.py with the connection string function (prior code block).
354
+ # Contents of test_app.py
355
+ # Our app.py with the connection string function (prior code block)
356
356
import app
357
357
358
358
@@ -363,27 +363,27 @@ For testing purposes we can patch the ``DEFAULT_CONFIG`` dictionary to specific
363
363
monkeypatch.setitem(app.DEFAULT_CONFIG , " user" , " test_user" )
364
364
monkeypatch.setitem(app.DEFAULT_CONFIG , " database" , " test_db" )
365
365
366
- # The expected result based on the mocks.
366
+ # The expected result based on the mocks
367
367
expected = " User Id=test_user; Location=test_db;"
368
368
369
- # The test uses the monkeypatched dictionary settings.
369
+ # The test uses the monkeypatched dictionary settings
370
370
result = app.create_connection_string()
371
371
assert result == expected
372
372
373
373
You can use the :py:meth: `monkeypatch.delitem ` to remove values.
374
374
375
375
.. code-block :: python
376
376
377
- # Contents of test_app.py.
377
+ # Contents of test_app.py
378
378
import pytest
379
379
380
- # Our app.py with the connection string function.
380
+ # Our app.py with the connection string function
381
381
import app
382
382
383
383
384
384
def test_missing_user (monkeypatch ):
385
385
386
- # Patch the DEFAULT_CONFIG t be missing the 'user' key.
386
+ # Patch the DEFAULT_CONFIG t be missing the 'user' key
387
387
monkeypatch.delitem(app.DEFAULT_CONFIG , " user" , raising = False )
388
388
389
389
# Key error expected because a config is not passed, and the
@@ -392,18 +392,18 @@ You can use the :py:meth:`monkeypatch.delitem` to remove values.
392
392
_ = app.create_connection_string()
393
393
394
394
395
- The modularity of the `` fixture `` syntax gives you the flexibility to define
396
- separate `` fixtures `` for each potential mock and reference them in the needed tests.
395
+ The modularity of fixtures gives you the flexibility to define
396
+ separate fixtures for each potential mock and reference them in the needed tests.
397
397
398
398
.. code-block :: python
399
399
400
- # Contents of test_app.py.
400
+ # Contents of test_app.py
401
401
import pytest
402
402
403
- # Our app.py with the connection string function.
403
+ # Our app.py with the connection string function
404
404
import app
405
405
406
- # All of the mocks are moved into separated fixtures.
406
+ # All of the mocks are moved into separated fixtures
407
407
@pytest.fixture
408
408
def mock_test_user (monkeypatch ):
409
409
""" Set the DEFAULT_CONFIG user to test_user."""
@@ -422,7 +422,7 @@ separate ``fixtures`` for each potential mock and reference them in the needed t
422
422
monkeypatch.delitem(app.DEFAULT_CONFIG , " user" , raising = False )
423
423
424
424
425
- # Tests reference only the fixture mocks that are needed.
425
+ # Tests reference only the fixture mocks that are needed
426
426
def test_connection (mock_test_user , mock_test_database ):
427
427
428
428
expected = " User Id=test_user; Location=test_db;"
@@ -431,7 +431,7 @@ separate ``fixtures`` for each potential mock and reference them in the needed t
431
431
assert result == expected
432
432
433
433
434
- def test_missing_user (mock_missing_default_user , mock_test_database ):
434
+ def test_missing_user (mock_missing_default_user ):
435
435
436
436
with pytest.raises(KeyError ):
437
437
_ = app.create_connection_string()
0 commit comments