Skip to content

Commit b203151

Browse files
committed
howto.rst: add documentation for emulating network errors
1 parent d77a677 commit b203151

1 file changed

Lines changed: 89 additions & 0 deletions

File tree

doc/howto.rst

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,92 @@ In the above code, all the request.get() calls could be in a different thread,
366366
eg. running in parallel, but the exit condition of the context object is to wait
367367
for the specified conditions.
368368

369+
370+
Emulating connection refused error
371+
----------------------------------
372+
373+
If by any chance, you want to emulate network errors such as *Connection reset
374+
by peer* or *Connection refused*, you can simply do it by connecting to a random
375+
port number where no service is listening:
376+
377+
.. code-block:: python
378+
379+
import pytest
380+
import requests
381+
382+
def test_connection_refused():
383+
# assumes that there's no server listening at localhost:1234
384+
with pytest.raises(requests.exceptions.ConnectionError):
385+
requests.get("http://localhost:1234")
386+
387+
388+
However connecting to the port where the httpserver had been started will still
389+
succeed as the server is running continuously. This is working by design as
390+
starting/stopping the server is costly.
391+
392+
.. code-block:: python
393+
394+
import pytest
395+
import requests
396+
397+
# setting a fixed port for httpserver
398+
@pytest.fixture
399+
def httpserver_listen_address():
400+
return ("127.0.0.1", 8000)
401+
402+
# this test will pass
403+
def test_normal_connection(httpserver):
404+
httpserver.expect_request("/foo").respond_with_data("foo")
405+
assert requests.get("http://localhost:8000/foo").text == "foo"
406+
407+
408+
# this tess will FAIL, as httpserver started in test_normal_connection is
409+
# still running
410+
def test_connection_refused():
411+
with pytest.raises(requests.exceptions.ConnectionError):
412+
# this won't get Connection refused error as the server is still
413+
# running.
414+
# it will get HTTP status 500 as the handlers registered in
415+
# test_normal_connection have been removed
416+
requests.get("http://localhost:8000/foo")
417+
418+
419+
420+
To solve the issue, the httpserver can be stopped explicitly. It will start
421+
implicitly when the first test starts to use it. So the
422+
``test_connection_refused`` test can be re-written to this:
423+
424+
.. code-block:: python
425+
426+
def test_connection_refused(httpserver):
427+
httpserver.stop() # stop the server explicitly
428+
with pytest.raises(requests.exceptions.ConnectionError):
429+
requests.get("http://localhost:8000/foo")
430+
431+
432+
Emulating timeout
433+
-----------------
434+
435+
To emulate timeout, there's one way to register a handler function which will sleep for a
436+
given amount of time.
437+
438+
.. code-block:: python
439+
440+
import time
441+
from pytest_httpserver import HTTPServer
442+
import pytest
443+
import requests
444+
445+
446+
def sleeping(request):
447+
time.sleep(2) # this should be greater than the client's timeout parameter
448+
449+
450+
def test_timeout(httpserver: HTTPServer):
451+
httpserver.expect_request("/baz").respond_with_handler(sleeping)
452+
with pytest.raises(requests.exceptions.ReadTimeout):
453+
assert requests.get(httpserver.url_for("/baz"), timeout=1)
454+
455+
456+
There's one drawback though: the test takes 2 seconds to run as it waits the
457+
handler thread to be completed.

0 commit comments

Comments
 (0)