Dependency Failure Simulation
Dependency failure simulation tests how a Java backend behaves when external dependencies such as databases, APIs, caches, or queues become unavailable or return errors. It helps verify that failures are handled predictably rather than propagating into uncontrolled outages.
A backend rarely operates alone: it may call a database, another HTTP service, a cache, or a message broker. Dependency failure simulation deliberately makes one of those dependencies fail so the application’s failure-handling behavior can be observed.
Typical injected failures include connection refusal, timeouts, HTTP 5xx responses, malformed responses, unavailable database connections, and message-broker errors. The goal is not merely to prove that an exception occurs; it is to verify the intended behavior of the service when that exception occurs.
For a Java service, dependency failures can be simulated at different boundaries. Unit tests commonly replace a dependency with a mock that throws an exception, while integration or resilience tests can use a test server, proxy, container, or fault-injection mechanism to reproduce failures closer to production conditions.
A useful test checks both the immediate response and the resulting system behavior. For example, an HTTP client timeout might need to produce a controlled `503 Service Unavailable`, trigger a retry policy, return cached data, or publish a message for later processing, depending on the system’s requirements.
Failure injection should also cover recovery and repeated failures. Important questions include whether retries stop, whether timeouts have finite bounds, whether a circuit breaker opens, whether resources are released, and whether logs and metrics make the incident observable.
Key points
- Inject failures at dependency boundaries, not only in application business logic.
- Test timeouts, exceptions, unavailable services, and invalid dependency responses.
- Verify the intended fallback, retry, timeout, or error response.
- Use mocks for focused unit tests and realistic infrastructure for integration or resilience tests.
- Check recovery behavior and observability as well as the immediate failure response.
Interview questions
What kinds of dependency failures should a backend test cover?
I would cover connection failures, timeouts, dependency-side 5xx responses, malformed responses, and resource exhaustion where relevant. For asynchronous systems, I would also consider message publication or consumption failures. The exact set depends on the dependency and its failure modes. I would prioritize failures that can cause incorrect results, long request latency, retry storms, or cascading outages.
What is dependency failure simulation, and why would you use it?
It means deliberately making an external dependency fail during a test. The dependency might be a database, HTTP API, cache, or message broker. The test verifies that the application handles the failure according to its requirements instead of producing uncontrolled errors. It is especially useful for checking exception handling, timeouts, fallbacks, and user-facing error responses.
How would you simulate a failed dependency in a Java unit test?
I would replace the real dependency with a test double, such as a Mockito mock, and configure it to throw an exception or return an error. Then I would call the application code and assert its resulting behavior. For example, a mocked HTTP client could throw a timeout exception. The test should verify the expected response or fallback rather than simply checking that an exception occurred.
How do retries and circuit breakers affect dependency failure testing?
Retries can make transient failures recover automatically, but they also add latency and can multiply load during an outage. A circuit breaker can stop repeated calls after failures and allow the dependency time to recover. Failure tests should therefore verify retry limits, backoff, timeout behavior, and circuit state transitions. The expected behavior should be defined explicitly rather than assuming that more retries are always better.
How would you test a dependency failure that only appears under realistic network conditions?
I would move beyond a mocked unit test and introduce the failure at an integration boundary. Depending on the environment, that could involve a controllable test service, proxy, containerized dependency, or network fault-injection mechanism. I would reproduce conditions such as latency, connection resets, dropped requests, and unavailable endpoints, then verify application behavior and telemetry. I would also test recovery so the system demonstrates both failure containment and restoration.