AWS re:Invent happy hour: Dec 1, 6 PM PST at MGM Grand RSVP

How Load Testing and Mocks Work Together

How Load Testing and Mocks Work Together

(Updated: )

If you’ve worked with load testing before, you know that there are a lot of things to consider. Whether or not you should combine load testing and mocks is one of those considerations.

Getting to the answer requires knowledge of your infrastructure and your development procedures, which will all influence what questions you need to ask yourself. However, one question that almost anyone needs to ask is, “Do I want to test the single service, or my entire infrastructure?”

If that question doesn’t make sense to you yet, go ahead and read on. In this post you’ll get an idea of how load testing and mocks work together, as well as the benefits you get from combining the two.

Load Testing without Mocks

Over the years there have been many tools available to create load tests. These days, one of the most commonly used local tools is k6. With k6, creating a load test can be as easy as creating a load-test.js file with the following contents:

import http from 'k6/http';
import { sleep } from 'k6';
export default function () {
  http.get('https://test.k6.io');
  sleep(1);
}

Unless you’re completely new to JavaScript or programming in general, it doesn’t get much easier. There are even tools to create k6 tests from recorded traffic However, this simple approach does present one challenge. What if https://test.k6.io has any dependencies?

There’s a high likelihood it will. Some common dependencies are:

  • A database,
  • Third-party APIs like payment gateways or data providers,
  • Storage services like S3.

From anecdotal experience, almost all services will have at least one of those dependencies, and most will have even more.

This is great in production, as it allows for separation of concerns, and there are few arguments against it. However, it can create unwanted results when you start to implement load tests.

Assuming that https://test.k6.io has a single dependency: a database. Sending 1,000 API requests does not imply 1,000 database queries. Caches, batching, and per-request query counts determine the actual database load.

Shouldn’t all of the Infrastructure Withstand High Load?

It’s easy to assume that this isn’t a problem in reality, as you don’t want to test whether a frontend application can handle 1,000 requests/s, only to discover that the database can’t handle it. While that is true, it only covers one of the use cases of load testing.

Imagine that you’re running a webshop. You know that the entire infrastructure as a whole is capable of handling 1,000 requests/s with no issues. One day, you have to modify how the backend API handles data validation on incoming requests.

Once you’ve finished coding the new feature, you need to verify that it can still handle the same amount of load, and this is where mocks come into the picture.

Having to spin up replicas for all the dependencies of the backend API is not only complex , it can also quickly become expensive. As you know the rest of the infrastructure still works, you can focus on testing the API itself, and mock any dependencies.

Mocks can reduce setup work and external variability, but do not guarantee a faster test. Measure the service and responder with the timing and workload you actually need. This is just one example of the advantages of using API mocks. Other advantages are:

  • They avoid live third-party API calls when routing and isolation are enforced;
  • They simulate responses;
  • They can transform requests.

On top of that, if your mocks are built from recorded traffic you can simulate a wide variety of responses.

Implementing Mocks

With a firm understanding of why you might want to combine load testing and mocks, you also need to understand how it can be done. There are a few different ways it can be accomplished.

Using Docker Containers as Mocks

If you are running in Docker-based environments like Kubernetes, it should be fairly easy to spin up a new Docker container that can act as a mock. It just has to be configured to provide the correct responses to any given request.

The advantage of this approach is that it’s “free” , that is, free in the sense that you only have to pay for the computing resources. You don’t have to pay for a third-party tool.

The downside to this approach is that you now have a lot more to manage. Not only are there now additional containers to keep track of , you also have to make sure that the mocks are always kept up to date, meaning there’s more code to maintain.

Automate it with Speedscale

Rather than adding more complexity to your infrastructure, you can start using mocks in your load test almost immediately if you set up Speedscale.

You can read the complete guide on load testing with Speedscale if you’re curious about specifics, but in short, Speedscale uses an operator and supported capture components in your Kubernetes cluster. Current installations use the eBPF collector; verify runtime coverage and Java-agent requirements before recording.

During a replay, configure the recorded dependencies to mock and inspect the match results. The responder supplies matching recorded responses. Unmatched calls and clients that bypass the mock need explicit controls; see the backend mocking guide.

Speedscale mock flow

Check both response correctness and mock coverage before adding the load test to CI. Keep a separate run with real test dependencies when you need end-to-end capacity evidence.

Focus Your Load Tests with Mocks

Whether or not you want to combine load testing and mocks is a choice you’ll have to make, depending on what your infrastructure and your needs are. But now you at least have a good amount of base knowledge to help you make an informed decision.

For an executable example, follow the k6 tutorial with mocked backends.

Frequently asked questions

When does mocking make a load test misleading?

When the mock removes the bottleneck or failure behavior you intended to measure. Immediate fixture responses can hide dependency latency, rate limits, contention, and retries. State which components are real, configure representative mock timing and failures, and measure the mock’s own capacity. A service-only result is not an end-to-end capacity result.

Should the database stay real?

Keep a disposable real database when the question concerns query execution, indexes, locking, transactions, or database capacity. Use recorded database responses when you want to isolate application behavior from that engine. A mock returning a successful write does not establish persistence or arbitrary read-after-write behavior. Run both configurations when both questions matter.

How do I test my service separately from a third-party API?

Route its outbound calls to recorded mocks, verify matching responses, and prevent unmatched traffic from reaching the provider. For HTTP through proxymock, use --no-passthrough; also block direct routes that bypass the proxy. Set realistic dependency timing and deliberate error cases, then load the real service and inspect its latency and failures.

Sources: Configure dependency mocks, proxymock CLI reference, Generator sizing and bottlenecks, Database replay and regression testing, proxymock architecture.

Stop writing API mocks by hand

proxymock records real traffic from your running app and replays it as mocks for HTTP, gRPC, Postgres, Kafka, and more. Install locally, activate once, and start recording.