Load testing with locust. Part 3

  • Tutorial
The final article about the tool for load testing Locust. Today I will share the observations that have accumulated in the process. As always, the video is attached.

Part 1 - testing with Locust
Part 2 - advanced scenarios


When writing my first tests with Locust, I was faced with the need to log in on one resource, having received an authorization token, which I would later use in load testing. Then the question immediately arose - how to do it, because the tool is sharpened to send all requests for one resource, which we specify in the console when the test is run. There are several solutions to the problem:

  • Disabling authorization on the tested resource - if there is such a possibility
  • generate a token in advance and attach it to the test code before launch - the weakest option that requires manual labor at each launch, but has the right to exist in some rare cases
  • send a request using the requests library and get the token from the answer - good, the syntax is the same

I chose the third option. Below I offer a reworked example from the first article with different possibilities of obtaining a token. Google.com will act as an authorization server and, since
there is no token, I will receive the simplest values

from locust import HttpLocust, TaskSet, task
import requests
       response = requests.post("http://mysite.sample.com/login", {"username": "ellen_key", "password": "education"})
       # get "token" from response header
       self.client.headers.update({'Authorization': response.headers.get('Date')})
       # get "token" from response cookies
       self.client.cookies.set('Authorization', response.cookies.get('NID'))
       # get "token" from response body
       self.client.headers.update({'Authorization': str(response.content.decode().find('google'))})

As you can see from the example, before starting work, the user sends a request to a third-party server and processes the response, placing the data in headers or cookies.


When working with request headers you need to take into account several important nuances.
For each separate request, you can specify your own set of headers as follows

self.client.post(url='/posts', data='hello world', headers={'hello': 'world'})

When executing this example, the hello header will be added to the already existing client session headers, but only in this query - it won't be in all the following ones. To make the title permanent, you can add it to the session:

self.client.headers.update({'aaa': 'bbb'})

Another interesting observation is that if we specify in the request a header that is already in the session, it will be overwritten, but only to this request. So you can not be afraid to accidentally wipe something important.

But there are exceptions. If we need to send a multipart form, the request will automatically form a Content-Type header , in which the form data separator will be specified. If we forcefully rewrite the header with the help of the headers argument , the request will fail, since the form cannot be correctly processed.

It is also worth noting that all headers are required strings. If you try to specify a number, for example {'aaa': 123} , the request will not be sent and the code will throw an exceptionInvalidHeader

Distributed testing

For distributed testing, locust provides several CLI arguments: --master and --slave , for clearly defining roles. In this case, the machine with the master will not simulate the load, but only collect statistics and coordinate the work. Let's try to start our test server and several sessions in distributed mode by running the following commands in different consoles:

json-server --watch sample_server/db.json
locust -f locust_files\locust_file.py --master --host=http://localhost:3000
locust -f locust_files\locust_file.py --slave --master-host=localhost
locust -f locust_files\locust_file.py --slave --master-host=localhost

Open the locust in the browser ( localhost: 8089 ), you can note that in the upper right corner we have the number of machines that will carry out the load

Testing without UI

When all the tests are written and debugged, it would be nice to include them in the regression automatic testing and just periodically check the results. With the following command, you can run the locust test without UI:

locust -f locust_files\locust_file.py --host=http://localhost:3000 --no-web -c 10 -r 2 --run-time 1m --csv=test_result


  • --no-web - argument that allows to run tests without UI
  • -c 10 - the maximum number of users
  • -r 2 - user growth per second
  • --run-time 1m - test run time (1 minute)
  • --csv = test_result - after running the test, 2 csv files with results will be created in the current folder, their names begin with test_result

Final facts, observations and conclusions

Distributed testing can be combined with regression testing - in order to ensure that all nodes for the load have started, you can add the argument —expect-slaves = 2 to the master , in which case the test will start only when at least 2 nodes are started.

I faced a situation a couple of times - the tested resource works only on HTTPS, the certificate was generated by the customer and the operating system marks it as suspicious. For tests to work successfully, you can add an argument to all requests that ignores the security check, for example:

self.client.get("/posts", verify=False)

Since I cannot always be sure in which environment the tests will be run, I always specify this argument.

That's all that I wanted to share. For myself, I discovered a simple and convenient tool with great testing capabilities and variability in creating queries and processing server responses. Thank you for reading to the end.

Also popular now: