You are currently viewing Stress Test your Python Application with Locust

Stress Test your Python Application with Locust

Imagine you are working on a big monolithic application that has hundreds of features and no test cases written on it. And you change something in one module. Will you get the confidence that this change will not impact the currently running application? For sure your will some goosebumps.

Photo by Bradyn Trollip on Unsplash

To avoid these situations we write test cases and integrate them into CI/CD pipeline. So that for every change the pipeline will run all the test cases to check whether the change will not break anything. And to rescue us in these situations Pytest or any other(as if they exists) testing toolkit will come into the picture.

But does it really solve all the issues?

Photo by Tim Gouw on Unsplash

Your test cases don’t actually replicate the same environment that your production is having. Think of it this way your test cases are actually checking whether the feature works or not. It is not checking the feature under stress usage. 

Generally, the test cases are meant for only one process. But in production, there are hundreds or even thousands of concurrent users are using the same feature.

So the question is, how will you replicate the same environment as production and test features of that.

Here come the load testing tools into the picture. There are many tools in the market for the same but I am going to explain Locust for that as it’s written in python and you can write scripts in Python. And most importantly it is open source and you can find the full documentation here. Enough talks, let’s dive into the topic

Prerequisites

We would need some applications for this demo so let’s create some super heavy applications like the one below

Run your application with gunicorn with 4 workers at least to do load testing

To do that install gunicorn first

Run it via the below command

Locust

Generally, test cases are written inside the repository itself. But load testing as it’s a kind of functional testing you can put in a totally separate repository.

Install the library using our favourite package manager

The entry point of the locust load testing toolkit is locustfile. So let’s create a simple test for our application in locustfile

Here we wrote three tests in one class for our super heavy and complicated application. Because our application is only having /hello, /world and /square API in it. Once you are done with it then to run it just fire the locust command

Just open the URL that is there in the terminal and there you can point to the application URL, concurrent request you want to fire and the ramp-up period of them.

Let’s say my application is used to have 1000 concurrent users on production so I want to load test my application with concurrent 1000 users. Then click start swarming

Then you can see the locust has swarmed your application.

Then you can also check out your application throughput using that. But also the spawn rate, requests per second and many other metrics. You can also check which APIs are failing in heavy loads and what are the exceptions to that.

But’s generally we don’t want these fancy UIs when we want to integrate our pretty locust in CI/CD. Then this locust has command-line flags too, to specify the URL etc.

You can fire a command like this 

This will start the locust for 3 minutes and it will spawn 100 users and the application host would be http://localhost:5000

The output will be like this:

More Complicated Example

Honestly, the application that we have created was in no way near to any production application. Most commonly the production applications have some kind of user management where some APIs were usually behind some authentication mechanism. Let’s integrate JWT into our application. As this blog is not about JWT so I will just use some basic library for that and also as a matter of fact I love this library.

Now add one /login endpoint to our application and put the other square endpoint behind jwt.

Just in case, below are the curl command for login and for square API now because first, we have to log in obtain an access token then pass the access token to square API.

Let’s come back to locustfile

Modify your locustfile so that it will first log in to the application, acquire the access token and use that access token in some of the APIs

You must have noticed that I have added a class variable wait_time 

That comes very handy, if you want to reproduce a real-life scenario like you don’t want to bombard your application with requests one after another. Generally, users use the application by taking some time in between the requests. So this command will wait for around 1 to 5 seconds between any consecutive test case.

You have also noticed @task(3) task. the task is the micro threads that will run by the locust program(test case). So assigning 3 to one task means we are assigning weight as 3 to that task. So the likelihood of locust picking up that task is 3 times more than of the other task. So in a real-life scenario, some APIs like dashboard API or home API or profile API are fired more in general. That you can replicate using this configuration.

We have also added the on_start function that will just task the token from the login API and assign that to the instance variable so that the token can be used by other test cases.

Production Configuration

Use of FastHttpUser

Instead of the default HTTP client that is HttpUser you can use FastHttpUser. As the name suggests it is fast when you are bombarding your application with a large number of users.

FastHttpUser uses a different HTTP client (geventhttpclient) compared to HttpUser (python-requests).
It’s significantly faster, but not as capable.

Usage

Categorize test cases into classes

Categorize your test into different classes into separate modules. And assign specific weights to the whole class to replicate a real-time scenario. By that, you can also run a specific class of test cases also 

Usage

Debugging

As you might already be knowing that the locust is running on Flask-2 and using gevent to create concurrent requests. So the debugging is quite difficult with that. To solve this they have this run_single_user a function that will not create any thread and to run this you just have to simply run the python application using the python command

Conclusion

This was the gist of the locust load testing. If you liked it so do give it a try and also read the full documentation here

If you liked this story. Then you can buy me a coffee using this link. That will motivate me to write more blogs like this. Peace out.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.