@@ -29,46 +29,89 @@ no further!
29
29
Quickstart
30
30
==========
31
31
32
+ `myproject.py `
33
+
34
+ .. code :: python
35
+
36
+ import requests
37
+
38
+
39
+ def make_request (url ):
40
+ """ A function that makes a web request."""
41
+ rsp = requests.get(url)
42
+ return rsp.text
43
+
44
+ `test_myproject.py `
45
+
46
+ .. code :: python
47
+
48
+ from myproject import request
49
+
50
+
51
+ def test_make_request (httpserver ):
52
+ httpserver.serve_content(
53
+ content = " success" ,
54
+ code = 200 ,
55
+ )
56
+
57
+ assert make_request(httpserver.url) == " success"
58
+
59
+ How-To
60
+ ======
61
+
32
62
Let's say you have a function to scrape HTML which only required to be pointed
33
- at a URL ::
63
+ at a URL :
64
+
65
+ .. code :: python
34
66
35
67
import requests
68
+
36
69
def scrape (url ):
37
- html = requests.get(url).text
38
- # some parsing happens here
39
- # ...
40
- return result
70
+ html = requests.get(url).text
71
+ # some parsing happens here
72
+ # ...
73
+ return result
41
74
42
75
You want to test this function in its entirety without having to rely on a
43
76
remote server whose content you cannot control, neither do you want to waste
44
77
time setting up a complex mechanism to mock or patch the underlying Python
45
78
modules dealing with the actual HTTP request (of which there are more than one
46
79
BTW). So what do you do?
47
80
48
- You simply use pytest's `funcargs feature `_ and simulate an entire server
49
- locally! ::
81
+ You simply use pytest's `fixture feature `_ and simulate an entire server
82
+ locally!
83
+
84
+ .. code :: python
50
85
51
86
def test_retrieve_some_content (httpserver ):
52
- httpserver.serve_content(open(' cached-content.html' ).read())
53
- assert scrape(httpserver.url) == ' Found it!'
87
+ httpserver.serve_content(open (" cached-content.html" ).read())
88
+ assert scrape(httpserver.url) == " Found it!"
54
89
55
90
What happened here is that for the duration of your tests an HTTP server is
56
91
started on a random port on localhost which will serve the content you tell it
57
92
to and behaves just like the real thing.
58
93
59
94
The added bonus is that you can test whether your code behaves gracefully if
60
- there is a network problem::
95
+ there is a network problem:
96
+
97
+ .. code :: python
61
98
62
- def test_content_retrieval_fails_graciously(httpserver):
63
- httpserver.serve_content(' File not found!' , 404)
64
- pytest.raises(ContentNotFoundException, scrape, httpserver.url)
99
+ def test_content_retrieval_fails_graciously (httpserver ):
100
+ httpserver.serve_content(" File not found!" , 404 )
101
+ pytest.raises(ContentNotFoundException, scrape, httpserver.url)
65
102
66
- The same thing works for SMTP servers, too::
103
+ The same thing works for SMTP servers, too:
104
+
105
+ .. code :: python
67
106
68
107
def test_sending_some_message (smtpserver ):
69
108
mailer = MyMailer(host = smtpserver.addr[0 ], port = smtpserver.addr[1 ])
70
-
71
- subject='MyMailer v1.0', body='Check out my mailer!')
109
+ mailer.send(
110
+
111
+
112
+ subject = " MyMailer v1.0" ,
113
+ body = " Check out my mailer!"
114
+ )
72
115
assert len (smtpserver.outbox)== 1
73
116
74
117
Here an SMTP server is started which accepts e-mails being sent to it. The
@@ -77,11 +120,12 @@ and what was sent by looking into the smtpserver's ``outbox``.
77
120
78
121
It is really that easy!
79
122
80
- Available funcargs
81
- ==================
123
+ Fixtures
124
+ ========
82
125
83
- Here is a short overview of the available funcargs. For more details I suggest
84
- poking around in the code itself.
126
+ Here is a short overview of the available pytest fixtures and their usage. This
127
+ information is also available via `pytest --fixtures `. For more details I
128
+ suggest poking around in the code itself.
85
129
86
130
``httpserver ``
87
131
provides a threaded HTTP server instance running on localhost. It has the
@@ -95,9 +139,17 @@ poking around in the code itself.
95
139
96
140
Once these attributes are set, all subsequent requests will be answered with
97
141
these values until they are changed or the server is stopped. A more
98
- convenient way to change these is ::
142
+ convenient way to change these is :
143
+
144
+ .. code :: python
99
145
100
- httpserver.serve_content(content=None, code=200, headers=None, chunked=pytest_localserver.http.Chunked.NO, store_request_data=True)
146
+ httpserver.serve_content(
147
+ content = None ,
148
+ code = 200 ,
149
+ headers = None ,
150
+ chunked = pytest_localserver.http.Chunked.NO ,
151
+ store_request_data = True
152
+ )
101
153
102
154
The ``chunked `` attribute or parameter can be set to
103
155
@@ -149,27 +201,37 @@ Using your a WSGI application as test server
149
201
============================================
150
202
151
203
As of version 0.3 you can now use a `WSGI application `_ to run on the test
152
- server ::
204
+ server :
205
+
206
+ .. code :: python
207
+
208
+ import pytest
209
+ from pytest_localserver.http import WSGIServer
210
+
211
+ from myproject import make_request
212
+
213
+
214
+ def simple_app (environ , start_response ):
215
+ """ Respond with success."""
216
+ status = " 200 OK"
217
+ response_headers = [(" Content-type" , " text/plain" )]
218
+ start_response(status, response_headers)
219
+ return [" success" .encode(" utf-8" )]
220
+
153
221
154
- from pytest_localserver.http import WSGIServer
222
+ @pytest.fixture
223
+ def testserver ():
224
+ """ Server for simple_app."""
225
+ server = WSGIServer(application = simple_app)
226
+ server.start()
227
+ yield server
228
+ server.stop()
155
229
156
- def simple_app(environ, start_response):
157
- """Simplest possible WSGI application"""
158
- status = '200 OK'
159
- response_headers = [('Content-type', 'text/plain')]
160
- start_response(status, response_headers)
161
- return ['Hello world!\n']
162
230
163
- @pytest.fixture
164
- def testserver(request):
165
- """Defines the testserver funcarg"""
166
- server = WSGIServer(application=simple_app)
167
- server.start()
168
- request.addfinalizer(server.stop)
169
- return server
231
+ def test_make_request (testserver ):
232
+ """ make_request() should return "success"."""
233
+ assert make_request(testserver.url) == " success"
170
234
171
- def test_retrieve_some_content(testserver):
172
- assert scrape(testserver.url) == 'Hello world!\n'
173
235
174
236
Have a look at the following page for more information on WSGI:
175
237
http://wsgi.readthedocs.org/en/latest/learn.html
@@ -253,9 +315,9 @@ For package maintainers, here is how we release a new version:
253
315
Having unsuccessfully tried to mock a server, I stumbled across
254
316
`linkchecker `_ which uses a the same idea to test its internals.
255
317
256
- .. _monkeypatching : http ://pytest.org/latest /monkeypatch.html
318
+ .. _monkeypatching : https ://docs. pytest.org/en/stable/how-to /monkeypatch.html
257
319
.. _pytest : http://pytest.org/
258
- .. _ funcargs feature : http ://pytest.org/latest/funcargs .html
320
+ .. _ fixture feature : https ://pytest.org/en/stable/explanation/fixtures .html
259
321
.. _linkchecker : http://linkchecker.sourceforge.net/
260
322
.. _WSGI application : http://www.python.org/dev/peps/pep-0333/
261
323
.. _PyPI : http://pypi.python.org/pypi/pytest-localserver/
0 commit comments