USD ($)
$
United States Dollar
Euro Member Countries
India Rupee

Mocking External Services and Load Testing with Locust

Lesson 18/30 | Study Time: 24 Min

Mocking external services and load testing with Locust are important practices for building reliable and scalable applications. Mocking external services involves simulating third-party APIs or dependencies during development and testing so that application behavior can be verified without relying on real external systems.

Locust is a load testing tool that helps measure how an application performs under different levels of user traffic by simulating concurrent users and real-world usage patterns.

Mocking External Services

Mocking replaces real external dependencies with controlled simulations, speeding up tests and eliminating variables like internet connectivity. This approach is essential for integration testing and CI/CD pipelines, following industry standards like those in pytest and unittest.mock from Python's standard library.


Why Mock External Services?

Real external services introduce unpredictability—downtime, slow responses, or changing APIs can break your tests. Mocking ensures deterministic testing, where outcomes depend only on your code.


For example, imagine your e-commerce API calls Stripe for payments. Mocking lets you simulate success, declines, or timeouts without real charges.

Core Mocking Techniques in Python

Python's unittest.mock library (built-in since Python 3.3) is the gold standard for object-level mocking, while responses and httpx-mock excel for HTTP clients. Choose based on your framework: FastAPI pairs well with pytest-httpx, Django with responses.



Step-by-Step: Mocking an HTTP Call with responses

Consider a FastAPI service fetching user data from a third-party auth API. We'll mock it for testing.


1. Install dependencies: pip install responses pytest.

2. Define your service function

python
import requests
from typing import Dict

def get_user_profile(user_id: str) -> Dict:
response = requests.get(f"https://api.external.com/users/{user_id}")
response.raise_for_status()
return response.json()


3. Write a test with mocking

python
import responses
import pytest

@responses.activate
def test_get_user_profile():
responses.add(
responses.GET,
"https://api.external.com/users/123",
json={"id": 123, "name": "Alice", "email": "alice@example.com"},
status=200
)
profile = get_user_profile("123")
assert profile["name"] == "Alice"


4. Run with pytest: It passes without calling the real API.

This technique scales to async code using pytest-httpx for httpx clients in FastAPI.


Advanced Mocking: Handling Failures and Retries

Simulate real-world issues to test resilience, like using tenacity for retries.


1. Mock delays: responses.add(..., body="...", status=503, headers={"Retry-After": "5"}).

2. Chain responses: Add sequential mocks for retry logic.

3. Dynamic mocks: Use callbacks for request-specific responses.

Pro Tip: In production CI, combine with Dockerized mocks (e.g., WireMock) for team consistency.

Load Testing Your API with Locust

Once your API is mocked and unit-tested, load testing validates performance under stress. Locust is an open-source Python tool (latest v2.20+ as of 2026) that lets you write concise user behavior scripts, scaling to millions of simulated users via gevent.


Understanding Locust and Its Advantages

Unlike JMeter's GUI, Locust uses pure Python code for tests, making it developer-friendly and version-controllable. It reports metrics like requests/sec, failure rates, and response times in real-time web UI.


Key benefits include


1. Scriptable: Define user actions as Python classes.

2. Distributed: Run across machines for massive scale.

3. Extensible: Plugins for AWS, Grafana integration.

Locust follows best practices from tools like Artillery, emphasizing realistic user simulations.


Setting Up Locust for Your Web API


1. Install: pip install locust[ui].

2. Create locustfile.py in your project root.

3. Basic structure

python
from locust import HttpUser, task, between

class ApiUser(HttpUser):
wait_time = between(1, 3) # Wait 1-3s between tasks

@task
def create_user(self):
self.client.post("/users/", json={"name": "Test User", "email": "test@example.com"})

@task(2) # Weight: twice as frequent
def get_users(self):
self.client.get("/users/")


4. Run: locust -f locustfile.py --host=http://localhost:8000.

5. Open http://localhost:8089, spawn users, and watch metrics.

Defining Realistic Load Scenarios

Craft tasks mimicking full-stack flows, like login → create resource → read → delete.

Common Patterns 



Example: Stress Testing with Ramp-up

bash
locust -f locustfile.py --host=http://localhost:8000 --users=1000 --spawn-rate=10 --run-time=5m --html=report.html

This spawns 1000 users over 5 minutes, generating an HTML report.


Analyzing Results and Best Practices

Locust's dashboard shows RPS (requests per second), 95th percentile response time, and failure buckets. Aim for <200ms P95 under load.


1. Thresholds: Alert if >5% failures or P95 >500ms.

2. Scaling Tips: Use --master/--worker for distributed runs; integrate with Prometheus.

3. Common Pitfalls: Avoid testing from localhost (use --host); mock databases for pure API load.

In a real project, combine with New Relic or Datadog for deeper insights.

Integrating Mocking and Load Testing in CI/CD

Tie these together in GitHub Actions or Jenkins for automated validation.


1. Mock externals in unit tests.

2. Run Locust in parallel CI jobs.

3. Fail builds on performance regressions.


Sample GitHub Workflow Snippet

text
- name: Load Test
run: locust -f locustfile.py --headless --users=500 --spawn-rate=50 --run-time=2m --html=report.html --failure-percentage=1

This ensures every PR maintains API health.

himanshu singh

himanshu singh

Product Designer
Profile

Class Sessions

1- HTTP Methods and REST Principles 2- Status Codes, Headers, and Request/Response cycles 3- JSON and XML Data Formats for API Payloads 4- Resource Naming Conventions and URI Design Best Practices 5- Statelessness, HATEOAS, and API Versioning Strategies 6- Rate Limiting, Caching, and Idempotency for Scalability 7- FastAPI Setup, Pydantic Models, and Async Endpoint Creation 8- Path/Query Parameters, Request/Response Validation 9- Dependency Injection and Middleware for Authentication/Authorization 10- SQLAlchemy ORM with Async Support for PostgreSQL/MySQL 11- CRUD Operations via API Endpoints with Relationships 12- Database Migrations Using Alembic and Connection Pooling 13- JWT/OAuth2 Implementation with FastAPI Security Utilities 14- File Uploads, Pagination, and Real-Time WebSockets 15- Input Sanitization, CORS, and OWASP Top 10 Defenses 16- Unit/integration testing with Pytest and FastAPI TestClient 17- API Documentation Generation with OpenAPI/Swagger 18- Mocking External Services and Load Testing with Locust 19- Containerization with Docker and Orchestration via Docker Compose 20- Deployment to Cloud Platforms 21- CI/CD Pipelines Using GitHub Actions and Monitoring with Prometheus 22- Consuming APIs in React/Vue.js with Axios/Fetch 23- State Management (Redux/Zustand) for API Data Flows 24- Error Handling, Optimistic Updates, and Frontend Caching Strategies 25- Async Processing with Celery/Redis for Background Tasks 26- Caching Layers (Redis) and Database Query Optimization 27- Microservices Patterns and API Gateways 28- Building a Full-Stack CRUD App with User Auth and File Handling 29- API Analytics, Logging (Structlog), and Error Tracking 30- Code Reviews, Maintainability, and Evolving APIs in Production