You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Waiting without pytest and without starting the server #251
I figured out the issue was with my code, the server needs to be started, and since my project doesn't use pytest, that needs to be done with the context manager manually.
server.expect_oneshot_request("/foobar").respond_with_json({"foo": "bar"})
- with server.wait(stop_on_nohandler=True, raise_assertions=True, timeout=1.0) as waiting:- requests.get(server.url_for("/foobar")) # raises requests.exceptions.ConnectionError+ with server:+ with server.wait(stop_on_nohandler=True, raise_assertions=True, timeout=1.0) as waiting:+ requests.get(server.url_for("/foobar")) # OK now
or more briefly:
server.expect_oneshot_request("/foobar").respond_with_json({"foo": "bar"})
- with server.wait(stop_on_nohandler=True, raise_assertions=True, timeout=1.0) as waiting:+ with server, server.wait(stop_on_nohandler=True, raise_assertions=True, timeout=1.0) as waiting:- requests.get(server.url_for("/foobar")) # raises requests.exceptions.ConnectionError+ requests.get(server.url_for("/foobar")) # OK now
I first thought I'd just let you know that I got confused about this part of the API.
Then I was wondering, what would you think about the .wait() context manager also doing the work of the HTTPServer context manager if the server isn't started yet? That would make the original code work, but I'm not sure about potential downsides. Maybe there are situations where you want to start .wait()-ing before you start a server?
For the first sight I thing doing server stop/start from the wait() method would be a layering violation, as wait() operates at how the requests are handled, while the server stopping and starting manages the server itself.
In pytest we also don't stop the server after each test as it causes some time penalty (0.1 sec or similar), so instead of stopping/starting we are clearing the server state instead. Because of this, it would be complicated to implement the stopping logic in the wait method.
I think with the unittest code you have, there are still possibilities.
you can run unittest code with pytest (although I have no experience about how the fixture works in that case)
you can put server starting to setUp and stopping to tearDown methods, so it will be started and stopped. You can store the httpserver instance to an attribute in such case. In this, case the server will be started/stopped for earch test.
you can put server starting and stopping to setUpClass and tearDownClass (which are classmethods) so the server will be started for the class and it will survive the tests. In such case you need to cleanup the server state by calling server.clear() call, this ensures that there's no cross-talk between the tests (removes the handlers, etc).
if there's no reason to use the wait method, you don't have to. If your code you test will finish, you can still examine the log attribute of the server object for the requests (and raise an error if there were none).
you can start the httpserver as a module variable, and then use it for the whole process, and let the test process to do the cleanup (this is not the nicest one, but still would work). In such case you would just need to call the clear() method descibed in step 3 between the tests.
What do you think, would any of these work for you?
put server starting to setUp and stopping to tearDown methods, so it will be started and stopped. You can store the httpserver instance to an attribute in such case. In this, case the server will be started/stopped for earch test.
This is what I actually have currently but maybe I'll switch to the classmethods and .clear()-ing to speed up things a bit. I agree that making the server a module level object wouldn't be very nice so I think I'll avoid that.
I haven't considered not wait()-ing at all, and looking at the .log instead, but that might make the test methods even simpler so I'll give it a shot. Actually we have a few places where our client code is supposed to not make any requests at all, so we might need to inspect the log anyway.
I think I mostly wanted to .wait() because of the stop_on_nohandler=True, raise_assertions=True options, i.e., turning unexpected requests into exceptions.
We do run the unittest test code via pytest (and tox) but as far as I know we can't use pytest test fixtures in this case.
I found that there's setUpModule and tearDownModule also, for module level setup and teardown.
So this would allow you to define more classes in the module, all receiving httpserver.
For some reason, having this code without any server.wait() hangs tox/pytest/unittest indefinitely after the tests have completed (the process doesn't quit).
Now I think I remember better, this is why I started using .wait() in the first place, to avoid the process hanging in some test cases (even if they are failed test cases). Maybe I can wrap each test case in a .wait() automatically by creating a waiter in the setUp() and finishing it in the tearDown().
# test_pytest_httpserver.py# python -m unittest test_pytest_httpserverimportcontextlibimportunittestimportrequestsfrompytest_httpserverimportHTTPServerclassTestMyStuff(unittest.TestCase):
defsetUp(self):
self.server=HTTPServer()
self._exit_stack=contextlib.ExitStack()
self.server.start()
self._exit_stack.callback(self.server.stop)
self._exit_stack.callback(self.server.check)
waiter=self.server.wait()
self._exit_stack.enter_context(waiter)
deftearDown(self):
self._exit_stack.close()
deftest_the_test_server_setup(self):
self.server.expect_oneshot_request("/foobar").respond_with_json({"foo": "bar"})
response=requests.get(self.server.url_for("/foobar"))
response.raise_for_status()
self.assertDictEqual(response.json(), {"foo": "bar"})
@unittest.expectedFailuredeftest_the_test_server_setup_for_failed_test_cases(self):
self.server.expect_oneshot_request("/foobar").respond_with_json({"foo": "bar"})
requests.get(self.server.url_for("/UNEXPECTED"))
# We need this call, otherwise the @unittest.expectedFailure won't have any effect# on the exception coming from the tearDown() after this test caseself._exit_stack.close()
The actual test case code is very minimal, only server.expect_...(...).respond_...(...) and requests.request(...) calls, and if I expect one-shot requests, and they don't happen, the test case will still fail without needing to assert the server logs len. The test case doesn't hang.
Activity
csernazs commentedon May 22, 2023
Hi Boldizsár,
For the first sight I thing doing server stop/start from the
wait()method would be a layering violation, as wait() operates at how the requests are handled, while the server stopping and starting manages the server itself.In pytest we also don't stop the server after each test as it causes some time penalty (0.1 sec or similar), so instead of stopping/starting we are clearing the server state instead. Because of this, it would be complicated to implement the stopping logic in the wait method.
I think with the unittest code you have, there are still possibilities.
setUpand stopping totearDownmethods, so it will be started and stopped. You can store the httpserver instance to an attribute in such case. In this, case the server will be started/stopped for earch test.setUpClassandtearDownClass(which are classmethods) so the server will be started for the class and it will survive the tests. In such case you need to cleanup the server state by callingserver.clear()call, this ensures that there's no cross-talk between the tests (removes the handlers, etc).logattribute of the server object for the requests (and raise an error if there were none).clear()method descibed in step 3 between the tests.What do you think, would any of these work for you?
palotasb commentedon May 23, 2023
This is what I actually have currently but maybe I'll switch to the classmethods and
.clear()-ing to speed up things a bit. I agree that making the server a module level object wouldn't be very nice so I think I'll avoid that.I haven't considered not
wait()-ing at all, and looking at the.loginstead, but that might make the test methods even simpler so I'll give it a shot. Actually we have a few places where our client code is supposed to not make any requests at all, so we might need to inspect the log anyway.I think I mostly wanted to
.wait()because of thestop_on_nohandler=True, raise_assertions=Trueoptions, i.e., turning unexpected requests into exceptions.We do run the
unittesttest code viapytest(andtox) but as far as I know we can't usepytesttest fixtures in this case.csernazs commentedon May 23, 2023
I found that there's setUpModule and tearDownModule also, for module level setup and teardown.
So this would allow you to define more classes in the module, all receiving httpserver.
Example:
There's some limited support for fixtures: https://docs.pytest.org/en/7.1.x/how-to/unittest.html#mixing-pytest-fixtures-into-unittest-testcase-subclasses-using-marks
But using setUp and tearDown (module and class levels, as well) is the idiomatic way to use the unittest module, I think.
palotasb commentedon May 23, 2023
For some reason, having this code without any
server.wait()hangs tox/pytest/unittest indefinitely after the tests have completed (the process doesn't quit).Is there something I'm doing wrong here? Note that after the line
FAILED (failures=1)the process hung and I hit^Cto quit.palotasb commentedon May 23, 2023
Now I think I remember better, this is why I started using
.wait()in the first place, to avoid the process hanging in some test cases (even if they are failed test cases). Maybe I can wrap each test case in a.wait()automatically by creating a waiter in thesetUp()and finishing it in thetearDown().palotasb commentedon May 23, 2023
This is the current best setup I have:
The actual test case code is very minimal, only
server.expect_...(...).respond_...(...)andrequests.request(...)calls, and if I expect one-shot requests, and they don't happen, the test case will still fail without needing to assert the server logs len. The test case doesn't hang.