Running tests in a debugger
Running Locust in a debugger is extremely useful when developing your tests. Among other things, you can examine a particular response or check some User instance variable.
But debuggers sometimes have issues with complex gevent-applications like Locust, and there is a lot going on in the framework itself that you probably aren’t interested in. To simplify this, Locust provides a method called run_single_user
:
from locust import HttpUser, run_single_user, task
class QuickstartUser(HttpUser):
host = "http://localhost"
@task
def hello_world(self):
with self.client.get("/hello", catch_response=True) as resp:
pass # maybe set a breakpoint here to analyze the resp object?
# if launched directly, e.g. "python3 debugging.py", not "locust -f debugging.py"
if __name__ == "__main__":
run_single_user(QuickstartUser)
It implicitly registers an event handler for the request event to print some stats about every request made:
type name resp_ms exception
GET /hello 38 ConnectionRefusedError(61, 'Connection refused')
GET /hello 4 ConnectionRefusedError(61, 'Connection refused')
You can configure exactly what is printed by specifying parameters to run_single_user
.
Make sure you have enabled gevent in your debugger settings.
Debugging Locust is quite easy with Vscode:
Place breakpoints
Select a python file or a scenario (ex:
`examples/basic.py
)Check that the Poetry virtualenv is correctly detected (bottom right)
Open the action Debug using launch.json. You will have the choice between debugging the python file, the scenario with WebUI or in headless mode
It could be rerun with the F5 shortkey
VS Code’s launch.json
looks like this:
{
"version": "0.2.0",
"configurations": [
{
"name": "Run current file",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"gevent": true
},
{
"name": "Run current locust scenario headless",
"type": "python",
"request": "launch",
"module": "locust",
"args": [
"-f",
"${file}",
"--headless",
"--users=5"
],
"console": "integratedTerminal",
"gevent": true
},
{
"name": "Run current locust scenario, autostart",
"type": "python",
"request": "launch",
"module": "locust",
"args": [
"-f",
"${file}",
"--users=5",
"--autostart",
"--print-stats",
"-L=ERROR"
],
"console": "integratedTerminal",
"gevent": true
}
]
}
If you want to the whole Locust runtime (with ramp up, command line parsing etc), you can do that too:
{
"version": "0.2.0",
"configurations": [
{
"name": "Locust: 5 users, with specific config file",
"type": "python",
"request": "launch",
"module": "locust",
"args": [
"-f",
"${file}",
"--headless",
"--users=5",
"--config=${fileDirname}/../locust.conf"
],
"console": "integratedTerminal",
"gevent": true
}
]
}
There is a similar setting in PyCharm.
Note
sys.settrace() should not be used when the debugger is being used
You can execute run_single_user multiple times, as shown in debugging_advanced.py.
Print HTTP communication
Sometimes it can be hard to understand why an HTTP request fails in Locust when it works from a regular browser/other application. Here’s how to examine the communication in detail:
For HttpUser
(python-requests):
# put this at the top of your locustfile (or just before the request you want to trace)
import logging
from http.client import HTTPConnection
HTTPConnection.debuglevel = 1
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
For FastHttpUser
(geventhttpclient):
import sys
...
class MyUser(FastHttpUser):
@task
def t(self):
self.client.get("http://example.com/", debug_stream=sys.stderr)
Example output (for FastHttpUser):
REQUEST: http://example.com/
GET / HTTP/1.1
user-agent: python/gevent-http-client-1.5.3
accept-encoding: gzip, deflate
host: example.com
RESPONSE: HTTP/1.1 200
Content-Encoding: gzip
Accept-Ranges: bytes
Age: 460461
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Fri, 12 Aug 2022 09:20:07 GMT
Etag: "3147526947+ident"
Expires: Fri, 19 Aug 2022 09:20:07 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Server: ECS (nyb/1D20)
Vary: Accept-Encoding
X-Cache: HIT
Content-Length: 648
<!doctype html>
<html>
<head>
...
These approaches can of course be used when doing a full load test, but you might get a lot of output :)