In distributed systems, performance issues often arise not from faulty business logic but from unpredictable network conditions—latency, jitter, and packet loss. Testing applications only in perfect environments leads to hidden production failures. This is where Testcontainers shines. Beyond spinning up databases and message brokers, Testcontainers provides network simulations that let developers test microservices under real-world conditions.
In this tutorial, we’ll explore network simulations and latency testing with Testcontainers, helping Java developers build resilient, production-ready applications.
What Are Network Simulations in Testcontainers?
Testcontainers integrates with Toxiproxy, a TCP proxy that introduces network faults like:
- Latency: Artificial delays in requests and responses
- Jitter: Random variation in latency
- Bandwidth throttling: Restricting available network speed
- Packet loss: Dropping requests/responses
By simulating these conditions, developers can test:
- How resilient their application is to slow downstream APIs
- Whether retry and fallback mechanisms work under network stress
- The impact of latency spikes in CI/CD pipelines
Setting Up Toxiproxy with Testcontainers
Maven Dependency
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>toxiproxy</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
Basic Setup in Java
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.ToxiproxyContainer;
import org.testcontainers.utility.DockerImageName;
import eu.rekawek.toxiproxy.Proxy;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class NetworkSimulationTest {
private static final ToxiproxyContainer toxiproxy =
new ToxiproxyContainer(DockerImageName.parse("ghcr.io/shopify/toxiproxy:2.5.0"));
@Test
void testLatencyInjection() throws IOException {
toxiproxy.start();
// Create a proxy for external service (e.g., API at localhost:8080)
Proxy proxy = toxiproxy.getProxy("localhost", 8080);
// Add 1000ms latency
proxy.toxics().latency("latency-toxic", ToxicDirection.DOWNSTREAM, 1000);
long start = System.currentTimeMillis();
assertThrows(Exception.class, () -> {
// Call API via proxy (simulate client call here)
makeHttpCall("http://localhost:" + proxy.getListenPort());
});
long duration = System.currentTimeMillis() - start;
System.out.println("Response time under latency: " + duration + "ms");
toxiproxy.stop();
}
private void makeHttpCall(String url) throws IOException {
throw new IOException("Simulated timeout");
}
}
Simulating Advanced Network Conditions
Adding Jitter
proxy.toxics().latency("jitter-toxic", ToxicDirection.DOWNSTREAM, 500)
.setJitter(200); // Adds random 200ms variation
Simulating Bandwidth Throttling
proxy.toxics().bandwidth("bandwidth-toxic", ToxicDirection.DOWNSTREAM, 50);
// Limit to 50 KB/s
Dropping Packets
proxy.toxics().limitData("drop-toxic", ToxicDirection.DOWNSTREAM, 1);
// Drops traffic after 1 byte
Real-World Scenarios
-
Microservices Communication
Simulate slow responses between services to validate retry/backoff logic. -
Database Latency
Use Toxiproxy in front of PostgreSQL or MySQL containers to test query timeout handling. -
CI/CD Integration
Ensure services degrade gracefully when APIs are unresponsive during pipeline tests.
Best Practices
- Always combine latency tests with JUnit 5 timeouts to prevent hanging builds.
- Use assertions on response times to validate system tolerance levels.
- Run stress tests in CI/CD with varying latency profiles to catch performance regressions early.
- Keep Toxiproxy lifecycle scoped per test class to avoid port conflicts.
Code Example: End-to-End Test with RestTemplate
import org.springframework.web.client.RestTemplate;
@Test
void testServiceWithLatency() throws IOException {
toxiproxy.start();
Proxy proxy = toxiproxy.getProxy("jsonplaceholder.typicode.com", 80);
proxy.toxics().latency("latency-toxic", ToxicDirection.DOWNSTREAM, 2000);
RestTemplate restTemplate = new RestTemplate();
long start = System.currentTimeMillis();
try {
restTemplate.getForObject("http://localhost:" + proxy.getListenPort() + "/todos/1", String.class);
} catch (Exception e) {
System.out.println("Request failed due to latency.");
}
long duration = System.currentTimeMillis() - start;
System.out.println("Duration with latency: " + duration + "ms");
toxiproxy.stop();
}
Version Tracker
- JUnit 4 → JUnit 5: Improved timeout handling and parallel execution.
- Mockito Updates: Support for static/final mocking helps when testing fallback logic.
- Testcontainers Growth: Added support for Toxiproxy integration to simulate network failures.
Conclusion & Key Takeaways
- Testcontainers with Toxiproxy enables realistic network condition testing.
- Developers can simulate latency, jitter, bandwidth throttling, and packet loss.
- Perfect for testing microservices resilience and CI/CD reliability.
- A critical tool for cloud-native applications running in unpredictable networks.
FAQ
Q1. What is Toxiproxy in Testcontainers?
A1. Toxiproxy is a TCP proxy used to simulate network conditions like latency and packet loss in tests.
Q2. Can I use Toxiproxy with Docker Compose?
A2. Yes, you can integrate Toxiproxy alongside other services in Docker Compose for complex integration tests.
Q3. How do I simulate high latency between microservices?
A3. Use the latency()
toxic in Toxiproxy with Testcontainers to introduce delays in communication.
Q4. Is it possible to combine multiple network faults?
A4. Yes, you can combine latency, jitter, and bandwidth limits to simulate real-world unstable networks.
Q5. How do I test database latency?
A5. Place Toxiproxy in front of your database container (PostgreSQL, MySQL) and inject delay using latency toxics.
Q6. Does Toxiproxy work with HTTPS?
A6. Yes, it works at TCP level, so HTTPS traffic is also proxied.
Q7. How can I use this in CI/CD?
A7. Add Testcontainers + Toxiproxy tests in your pipeline to validate service resilience under poor networks.
Q8. Can I control latency dynamically during tests?
A8. Yes, you can adjust toxic parameters at runtime for dynamic testing.
Q9. Is this approach production safe?
A9. Yes, since it only affects test environments, your production services remain unaffected.
Q10. Why not just use mocks for network failures?
A10. Mocks can simulate failures, but realistic latency/jitter testing requires actual network simulation.