Skip to main content

        One client class, two test frameworks. How to share API interaction code between Locust load tests and pytest functional tests.

Reusing Core Functions for Locust and pytest: A Path to Efficient Testing

One client class, two test frameworks. How to share API interaction code between Locust load tests and pytest functional tests.

In the world of software development, testing plays a crucial role in ensuring the quality and reliability of applications. Two widely used testing frameworks, Locust and pytest, have gained popularity due to their versatility and ease of use. However, as projects grow in complexity, it becomes essential to adopt practices that promote code reusability and maintainability.

This article explores how to leverage the power of reusable functions across Locust and pytest, streamlining the testing process and improving overall efficiency.

To create this article, I used mocked data via mockapi.io, and the sources can be found in my automation ideas repository: Formartha/python-automation-ideas.

Code breakdown

The provided code demonstrates a practical approach to creating reusable functions for both performance testing with Locust and functional testing with pytest.

api.py

This file encapsulates the logic for interacting with a RESTful API. The ProductApi class encapsulates the API endpoint and provides a users method that sends HTTP requests using the requests library.

import requests


class ProductApi(object):
    def __init__(self, host):
        self.host = host
        self.session = requests.session()

    def users(self, method):
        return self.session.request(method=method, url=self.host, verify=False)

performance_test.py

This file defines a Locust test case for load testing the API. The QuickstartUser class extends HttpUser from Locust and uses ProductApi to send API requests. The api task initiates the call and prints the response status code.

from locust import HttpUser, task, between
from api import ProductApi


class QuickstartUser(HttpUser):
    wait_time = between(1, 2)

    @task
    def api(self):
        lo_client = ProductApi("https://66333084f7d50bbd9b487735.mockapi.io/api/v1/users")
        lo_client.session = self.client
        print(lo_client.users("GET").status_code)

test_functional.py

This snippet demonstrates how to use the ProductApi class in a pytest test case. The test_with_pytest function creates an instance of ProductApi, sends a GET request, and asserts that the first user’s name matches the expected value.

from api import ProductApi


def test_with_pytest():
    client = ProductApi("https://66333084f7d50bbd9b487735.mockapi.io/api/v1/users")
    assert client.users('GET').json()[0]['name'] == 'Clemens_Crist'

The benefits of reusable functions

By encapsulating the API interaction logic in the ProductApi class, both Locust and pytest tests can leverage the same functionality. This approach offers several advantages:

Code reusability. ProductApi can be reused across multiple test cases, reducing duplication and promoting a more modular codebase.

Maintainability. If the API endpoint or request logic needs to be updated, changes happen in a single location (api.py), ensuring consistent behavior across all test cases.

Separation of concerns. Locust and pytest tests focus on their respective responsibilities (load testing and functional testing), while the API interaction logic is encapsulated in a separate module, promoting better code organization and readability.

Test consistency. By using the same API interaction logic across performance and functional tests, you ensure that both types of tests exercise the same code paths, reducing the risk of inconsistencies or discrepancies in test results.

Conclusion

Reusing functions across Locust and pytest not only promotes code reusability and maintainability but also fosters a more efficient and consistent testing approach. By following the principles demonstrated in this article, automation developers can streamline their testing efforts, reduce code duplication, and ensure that both performance and functional tests remain aligned with the evolving codebase.