Using locust in python: introduction, associated variables, and basic assertions
Locust is a good open source load testing tool, which can provide tests for developers with Python experience, because tests can be created as code. We have discussed this issue in some previous articles. But what if you've never used it? In this article, we will show you how to use this performance testing tool to start your first step by showing an example of a basic workflow developed in Python, an advanced workflow, and how to associate valuables and assert your feet.
# Start: installing Python
In order to run Locust, you need to install Python. If this is not your case, we will leave you a link to download it here. Then, all you have to do is run the following command:
pip3 install locust
# Script and perform load tests from the Locust GUI
The next step is to create a file named locustfile Py script to create a file. In this file, we will define the HTTP request executed in the load test. Using the script's file name enables Locust to find the file automatically. (if you want to use another name for the file, you need to add the parameter - f and file name during execution. I'll show you below.
In this example, we will load the test site https://www.demoblaze.com/ .
Create a basic script
First, we will write a script to call the demoblaze home page, as shown below:
from locust import HttpUser, task class User(HttpUser): @task def mainPage(self): self.client.get("/")
Note that the url of the tested site is not specified in the script. Instead, it is specified from the UI when the test is run. Locust will only run the function in the decorator @ task, so we must remember to add it. If multiple tasks are defined, they will be executed randomly by default.
Running the Script 2. Run script
To run this test, we need to execute the lock command in the script directory from the command line, which will launch the web user interface on port 8089. Just navigate to http://localhost:8089 The map can access it.
Note: if you get an error when another program uses the port, you can change it using the locust command -- Web port [port]. As I mentioned above, if you want to run a script with a different name, you can execute lock-f [file name].
The number of users, the number of users started per second, and the host (the url in the test) can be selected from locust's web user interface. Locust will create a complete url for each request using the path in the script and the host specified here.
Now you can run the test by clicking the Start swarming button, and the browser will display a dashboard as shown in the figure below:
In this dashboard, we will see a report including the number of requests executed, the number of failed requests, 90% and other real-time statistics. By default, the virtual user will continue to run until the test stops.
If you don't want to use the web UI to run the script, you can run the following command:
locust --headless -u 1 -r 1 -H https://www.demoblaze.com
Where - u specifies the number of users, - r represents the generation rate and - h represents the host.
Create more advanced scripts
Now that we know how to create and run basic tests, let's do a test with a more complex workflow.
This workflow consists of four steps:
Enter the website, log in, add products to the shopping cart, and submit the shopping cart
Once we get the HTTP request for each step in the workflow, we can create a method for each user action.
from locust import HttpUser, SequentialTaskSet, task, between class User(HttpUser): @task class SequenceOfTasks(SequentialTaskSet): wait_time = between(1, 5) @task def mainPage(self): self.client.get("/") self.client.get("https://api.demoblaze.com/entries") @task def login(self): self.client.options("https://api.demoblaze.com/login") self.client.post("https://api.demoblaze.com/login",json={"username":"aaaa","password":"YWFhYQ=="}) self.client.options("https://api.demoblaze.com/check") self.client.get("https://api.demoblaze.com/entries") self.client.post("https://api.demoblaze.com/check",json={"token":"YWFhYTE2MzA5NDU="}) @task def clickProduct(self): self.client.get("/prod.html?idp_=1") self.client.options("https://api.demoblaze.com/check") self.client.options("https://api.demoblaze.com/view") self.client.post("https://api.demoblaze.com/check",json={"token":"YWFhYTE2MzA5NDU="}) self.client.post("https://api.demoblaze.com/view",json={"id":"1"}) @task def addToCart(self): self.client.options("https://api.demoblaze.com/addtocart") self.client.post("https://api.demoblaze.com/addtocart",json={"id":"fb3d5d23-f88c-80d9-a8de-32f1b6034bfd","cookie":"YWFhYTE2MzA5NDU=","prod_id":1,"flag":'true'}) @task def viewCart(self): self.client.get("/cart.html") self.client.options("https://api.demoblaze.com/check") self.client.options("https://api.demoblaze.com/viewcart") self.client.post("https://api.demoblaze.com/check",json={"token":"YWFhYTE2MzA5NDU="}) self.client.post("https://api.demoblaze.com/viewcart",json={"cookie":"YWFhYTE2MzA5NDU=","flag":'true'}) self.client.options("https://api.demoblaze.com/view") self.client.post("https://api.demoblaze.com/check",json={"token":"YWFhYTE2MzA5NDU="}) self.client.post("https://api.demoblaze.com/view",json={"id":"1"})
As you can see, all methods are contained in the class using SequentialTaskSet, so they can be executed in the same order as declared.
In addition, by using wait_ Time, we can add a pause between tasks. In this case, there is a random pause between 1 and 5 seconds.
You will also notice that the request to the API is written using the full URL, because the locate web user interface allows only one URL in the host field.
Related variables
The next step is to associate the hard coded tokens. The parameters of association dynamics are very important. We know that this tag will change every time the user logs in. The token may expire, and if it is not parameterized, the script stops working.
By analyzing the HTTP request workflow, we can see that the token sent in / check post can be extracted from the / login response. We can then save it to a variable and use it in all of the following requests that need it.
The tag format of the response is as follows:
"Auth_token: YWFhYTE2MzA1ODg =" so you can extract it using the following regular expression:
"Auth_token: (.+?)" Next, you need to import the module re and use the match method to save the extracted value to the variable.
Therefore, in order to extract the tag from the response, we first save the response to the variable, as shown below:
response = self.client.post("https://api.demoblaze.com/login",json={"username":"aaaa","password":"YWFhYQ=="})
Now we can define a global variable and extract tags using regular expressions.
global token token = re.match("\"Auth_token: (.+?)\"",response.text)[1]
We can use this variable in the following requests
self.client.post("https://api.demoblaze.com/check",json={"token":token} This is what it looks like to log in and click on the product transaction:
@task def login(self): self.client.options("https://api.demoblaze.com/login") response = self.client.post("https://api.demoblaze.com/login",json={"username":"aaaa","password":"YWFhYQ=="}) global token token = re.match("\"Auth_token: (.+?)\"",response.text)[1] self.client.options("https://api.demoblaze.com/check") self.client.get("https://api.demoblaze.com/entries") self.client.post("https://api.demoblaze.com/check",json={"token":token}) @task def clickProduct(self): self.client.get("/prod.html?idp_=1") self.client.options("https://api.demoblaze.com/check") self.client.options("https://api.demoblaze.com/view") self.client.post("https://api.demoblaze.com/check",json={"token":token}) self.client.post("https://api.demoblaze.com/view",json={"id":"1"})
Note: in this example, we only use one user. If you want to learn how to run scripts in multiple users, you can see this article.
https://www.blazemeter.com/blog/how-to-run-locust-with-different-users
Use assertions
Now, I'll show you how to add a simple assertion to verify that the products added to the shopping cart are added correctly.
Locust doesn't have many built-in features, but it's easy to add custom features using Python.
Failure ("Error message") can be used to mark a request as failed. For assertions to work, this function should be used in the if clause and catch should be added_ Use the response parameter to verify the response, as shown below.
@task def viewCart(self): self.client.get("/cart.html") self.client.options("https://api.demoblaze.com/check") self.client.options("https://api.demoblaze.com/viewcart") self.client.post("https://api.demoblaze.com/check",json={"token":token}) with self.client.post("https://api.demoblaze.com/viewcart",catch_response=True,json={"cookie":token,"flag":'true'}) as response: if '"prod_id":1' not in response.text: response.failure("Assert failure, response does not contain expected prod_id") self.client.options("https://api.demoblaze.com/view") self.client.post("https://api.demoblaze.com/check",json={"token":token}) self.client.post("https://api.demoblaze.com/view",json={"id":"1"})
If we run the test, we can see that it has no errors. But how do we know if this claim is valid? Let's change the product id to an id that does not exist in the response.
with self.client.post("https://api.demoblaze.com/viewcart",catch_response=True,json={"cookie":token,"flag":'true'}) as response: if '"prod_id":1234' not in response.text: response.failure("Assert failure, response does not contain expected prod_id")
When the test is executed again, / viewpart post will fail, and the defined error message can be seen in the Failures tab.
If you want to use multiple assertions, you'd better create a function to avoid rewriting similar code, such as this:
def assertContains(response,text): with response as r: if text not in r.text: r.failure("Expected "+ response.text + " to contain "+ text)
You can call it like this, every time you want to make an assertion with text:
assertContains(self.client.post("https://api.demoblaze.com/viewcart",catch_response=True,json={"cookie":token,"flag":'true'}),'"prod_id":1')
Once you complete the locust script, you can run it on the BlazeMeter scale, integrate it on CI/CD and see advanced reports. Start now.