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.
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 to 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 propose a converted example from the first article with different possibilities of obtaining a token. As the authorization server will google.com and, so as there is no token, I will get the simplest values
from locust import HttpLocust, TaskSet, task import requests classUserBehavior(TaskSet):defon_start(self): 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.
Headers
When working with request headers, there are several important points to consider. For each separate request, you can specify your own set of headers as follows
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, then 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 InvalidHeader exception
Distributed testing
For distributed testing, locust provides several CLI arguments: --master and --slave , for clearly defining roles. At the same time 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 executing commands in different consoles:
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 automated testing and simply check the results periodically. With the following command, you can run the locust test without UI:
--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 the nodes for the load have started, you can add the argument --expect-slaves = 2 on 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, while the certificate was generated by the customer and the operating system marks it as suspicious. For the 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 can not always be sure in which environment the tests will be run, I always specify this argument.
That's all what I wanted to share. For myself, I discovered a simple and convenient tool with great testing capabilities and variations in creating requests and processing server responses. Thank you for reading to the end.