Testing Services with External REST Calls
Fast recall
Quick revision
Core answer
WebClient chain. Separately test HTTP details—URL, headers, JSON, timeout, and status mapping—against a local stub server such as WireMock or MockWebServer. No real third-party call is needed.Interview-ready guide
Interview answer
I separate the business service from the HTTP adapter.
The service depends on a small interface such as PricingGateway, so its unit test can mock one method and check what the service does with a price or error. Then I test the real HTTP adapter against a local WireMock or MockWebServer instance. That second test proves the URL, headers, JSON body, response mapping, timeout, and 4xx or 5xx behavior without contacting the vendor.
I avoid mocking every WebClient step because that verifies an implementation chain rather than the actual HTTP request. For failures, I include the cases the production policy handles—such as timeout, malformed JSON, and a retryable server response—and I keep retry behavior consistent with whether the request is safe to repeat.
Testing Services with External REST Calls
Direct Answer
Put an HTTP client behind a small application interface. Unit-test the service by mocking that interface, not by reproducing a fluent WebClient chain. Separately test HTTP details—URL, headers, JSON, timeout, and status mapping—against a local stub server such as WireMock or MockWebServer. No real third-party call is needed.
Code Example
@ExtendWith(MockitoExtension.class)
class PaymentServiceTest {
@Mock
private RestTemplate restTemplate;
private PaymentService paymentService;
@BeforeEach
void setUp() {
paymentService = new PaymentService(restTemplate);
}
@Test
void processPayment_callsExternalAPIAndReturnsStatus() {
var request = new PaymentRequest("txn-123", 99.99);
var apiResponse = new PaymentAPIResponse("SUCCESS", "txn-123");
// Stub the external API call
when(restTemplate.postForObject(
"https://payment-gateway.com/api/charge",
request,
PaymentAPIResponse.class
)).thenReturn(apiResponse);
var result = paymentService.processPayment(request);
assertEquals("SUCCESS", result.getStatus());
verify(restTemplate).postForObject(anyString(), eq(request), eq(PaymentAPIResponse.class));
}
}
Common Mistakes & Pitfalls
- Mocking a fluent chain: tests become tied to
WebClientimplementation details. - Testing only the happy response: cover timeout, connection failure, 4xx, 5xx, and invalid JSON where relevant.
- Calling the real service: tests become slow, rate-limited, costly, and nondeterministic.
- Asserting only a return value: adapter tests should also verify the outgoing HTTP contract.
- Retrying everything: retries must follow idempotency and error policy; do not hide this in the mock setup.
Check your understanding
Answer these without looking back at the article.
- What is WireMock and how does it compare to mocking RestTemplate directly?
- How do you test error handling when the external API returns a 4xx or 5xx response?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.