google-site-verification: google61fe8ba583a51912.html
Hoverfly for Service Virtualization in Java

Comprehensive Hoverfly Implementation Guide

1. Hoverfly Configuration and Setup

Maven Dependencies

<!-- pom.xml --> <properties> <hoverfly.version>0.14.3</hoverfly.version> <wiremock.version>2.35.0</wiremock.version> </properties> <dependencies> <!-- Hoverfly Java DSL --> <dependency> <groupId>io.specto</groupId> <artifactId>hoverfly-java</artifactId> <version>${hoverfly.version}</version> <scope>test</scope> </dependency> <!-- Hoverfly JUnit5 Extension --> <dependency> <groupId>io.specto</groupId> <artifactId>hoverfly-junit5</artifactId> <version>${hoverfly.version}</version> <scope>test</scope> </dependency> <!-- Spring Test for Integration Tests --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- WireMock for Comparison --> <dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock-jre8</artifactId> <version>${wiremock.version}</version> <scope>test</scope> </dependency> </dependencies>

2. Hoverfly Configuration Class

// HoverflyConfig.java package com.example.hoverfly.config; import io.specto.hoverfly.junit.core.Hoverfly; import io.specto.hoverfly.junit.core.HoverflyConfig; import io.specto.hoverfly.junit.core.HoverflyMode; import io.specto.hoverfly.junit.core.SimulationSource; import io.specto.hoverfly.junit5.HoverflyExtension; import io.specto.hoverfly.junit5.api.HoverflyConfig; import io.specto.hoverfly.junit5.api.HoverflyCore; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import java.nio.file.Paths; @Configuration public class HoverflyConfig { @Value("${hoverfly.admin.port:8888}") private int adminPort; @Value("${hoverfly.proxy.port:8500}") private int proxyPort; @Value("${hoverfly.simulation.directory:src/test/resources/hoverfly}") private String simulationDirectory; @Bean @Profile("test") public Hoverfly hoverfly() { HoverflyConfig config = HoverflyConfig.localConfigs() .adminPort(adminPort) .proxyPort(proxyPort) .captureHeaders("Content-Type", "Authorization") .enableStatefulCapture(10) .plainHttpTunneling() .logToStdOut(); return new Hoverfly(config, HoverflyMode.SIMULATE); } @Bean @Profile("capture") public Hoverfly hoverflyCapture() { HoverflyConfig config = HoverflyConfig.localConfigs() .adminPort(adminPort) .proxyPort(proxyPort) .captureHeaders("Content-Type", "Authorization", "X-Correlation-ID") .enableStatefulCapture(50) .destination("api.github.com") .destination("jsonplaceholder.typicode.com") .plainHttpTunneling(); return new Hoverfly(config, HoverflyMode.CAPTURE); } }

3. Service Virtualization Manager

// HoverflyService.java package com.example.hoverfly.service; import io.specto.hoverfly.junit.core.Hoverfly; import io.specto.hoverfly.junit.core.SimulationSource; import io.specto.hoverfly.junit.core.model.RequestFieldMatcher; import io.specto.hoverfly.junit.core.model.Response; import io.specto.hoverfly.junit.core.model.ResponseBuilder; import io.specto.hoverfly.junit.core.model.DelaySettings; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Service; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @Service public class HoverflyService { private static final Logger logger = LoggerFactory.getLogger(HoverflyService.class); private final Hoverfly hoverfly; private final ObjectMapper objectMapper; private final ResourceLoader resourceLoader; private final Map<String, SimulationSource> simulationCache = new ConcurrentHashMap<>(); @Autowired public HoverflyService(Hoverfly hoverfly, ObjectMapper objectMapper, ResourceLoader resourceLoader) { this.hoverfly = hoverfly; this.objectMapper = objectMapper; this.resourceLoader = resourceLoader; } public void startHoverfly() { if (!hoverfly.isRunning()) { hoverfly.start(); logger.info("Hoverfly started on proxy port: {}", hoverfly.getHoverflyConfig().getProxyPort()); } } public void stopHoverfly() { if (hoverfly.isRunning()) { hoverfly.close(); logger.info("Hoverfly stopped"); } } public void importSimulation(String simulationName) { try { String simulationPath = String.format("classpath:hoverfly/%s.json", simulationName); Resource resource = resourceLoader.getResource(simulationPath); if (resource.exists()) { SimulationSource source = SimulationSource.defaultPath(resource.getFile().toPath()); hoverfly.importSimulation(source); simulationCache.put(simulationName, source); logger.info("Imported simulation: {}", simulationName); } else { logger.warn("Simulation file not found: {}", simulationPath); } } catch (IOException e) { logger.error("Failed to import simulation: {}", simulationName, e); } } public void exportSimulation(String simulationName) { try { String exportPath = String.format("src/test/resources/hoverfly/%s-exported-%s.json", simulationName, LocalDateTime.now().format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME)); hoverfly.exportSimulation(Paths.get(exportPath)); logger.info("Exported simulation to: {}", exportPath); } catch (IOException e) { logger.error("Failed to export simulation: {}", simulationName, e); } } public void simulateGetRequest(String service, String path, int statusCode, String responseBody) { simulateGetRequest(service, path, statusCode, responseBody, Collections.emptyMap()); } public void simulateGetRequest(String service, String path, int statusCode, String responseBody, Map<String, String> headers) { hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .get(RequestFieldMatcher.newGlobMatcher(path)) .willReturn(ResponseBuilder.response() .status(statusCode) .body(responseBody) .headers(headers) .build()) )); logger.debug("Simulated GET {} with status: {}", path, statusCode); } public void simulatePostRequest(String service, String path, String requestBody, int statusCode, String responseBody) { simulatePostRequest(service, path, requestBody, statusCode, responseBody, Collections.emptyMap()); } public void simulatePostRequest(String service, String path, String requestBody, int statusCode, String responseBody, Map<String, String> headers) { hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .post(RequestFieldMatcher.newGlobMatcher(path)) .body(RequestFieldMatcher.newExactMatcher(requestBody)) .willReturn(ResponseBuilder.response() .status(statusCode) .body(responseBody) .headers(headers) .build()) )); logger.debug("Simulated POST {} with status: {}", path, statusCode); } public void simulateWithDelay(String service, String path, String method, int statusCode, String responseBody, int delayMs) { hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .andMethod(method) .andPath(path) .willReturn(ResponseBuilder.response() .status(statusCode) .body(responseBody) .header("Content-Type", "application/json") .andSetDelay(DelaySettings.delay(delayMs).build()) .build()) )); logger.debug("Simulated {} {} with {}ms delay", method, path, delayMs); } public void simulateErrorScenario(String service, String path, String method, int statusCode, String errorMessage) { Map<String, Object> errorResponse = new HashMap<>(); errorResponse.put("timestamp", LocalDateTime.now().toString()); errorResponse.put("status", statusCode); errorResponse.put("error", getHttpStatusText(statusCode)); errorResponse.put("message", errorMessage); errorResponse.put("path", path); try { String responseBody = objectMapper.writeValueAsString(errorResponse); hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .andMethod(method) .andPath(path) .willReturn(ResponseBuilder.response() .status(statusCode) .body(responseBody) .header("Content-Type", "application/json") .build()) )); logger.debug("Simulated error: {} {} -> {}", method, path, statusCode); } catch (Exception e) { logger.error("Failed to simulate error scenario", e); } } public void simulateStatefulBehavior(String service, String path, String initialState, Map<String, String> stateTransitions) { // Implement stateful simulation hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .get(path) .willReturn(ResponseBuilder.response() .status(200) .body(String.format("{\"state\": \"%s\"}", initialState)) .build()) )); // Add state transitions stateTransitions.forEach((trigger, newState) -> { hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .post(path) .body(RequestFieldMatcher.newJsonPartialMatcher( String.format("{\"action\": \"%s\"}", trigger))) .willReturn(ResponseBuilder.response() .status(200) .body(String.format("{\"state\": \"%s\"}", newState)) .build()) )); }); } public Map<String, Object> getHoverflyMetrics() { Map<String, Object> metrics = new HashMap<>(); metrics.put("isRunning", hoverfly.isRunning()); metrics.put("mode", hoverfly.getMode().name()); metrics.put("proxyPort", hoverfly.getHoverflyConfig().getProxyPort()); metrics.put("adminPort", hoverfly.getHoverflyConfig().getAdminPort()); metrics.put("cachedSimulations", simulationCache.size()); return metrics; } private String getHttpStatusText(int statusCode) { switch (statusCode) { case 400: return "Bad Request"; case 401: return "Unauthorized"; case 403: return "Forbidden"; case 404: return "Not Found"; case 500: return "Internal Server Error"; case 503: return "Service Unavailable"; default: return "Error"; } } }

4. External Service Client with Hoverfly

// ExternalServiceClient.java package com.example.hoverfly.client; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import java.util.Collections; import java.util.HashMap; import java.util.Map; @Component public class ExternalServiceClient { private static final Logger logger = LoggerFactory.getLogger(ExternalServiceClient.class); private final RestTemplate restTemplate; private final ObjectMapper objectMapper; @Value("${external.service.base-url:http://localhost:8500}") private String baseUrl; @Value("${external.service.timeout:5000}") private int timeout; @Autowired public ExternalServiceClient(RestTemplate restTemplate, ObjectMapper objectMapper) { this.restTemplate = restTemplate; this.objectMapper = objectMapper; } public <T> T get(String path, Class<T> responseType) { return get(path, responseType, Collections.emptyMap()); } public <T> T get(String path, Class<T> responseType, Map<String, String> headers) { String url = baseUrl + path; HttpHeaders httpHeaders = new HttpHeaders(); headers.forEach(httpHeaders::set); httpHeaders.setContentType(MediaType.APPLICATION_JSON); HttpEntity<String> entity = new HttpEntity<>(httpHeaders); try { ResponseEntity<T> response = restTemplate.exchange( url, HttpMethod.GET, entity, responseType); logger.debug("GET {} -> {}", url, response.getStatusCode()); return response.getBody(); } catch (Exception e) { logger.error("GET {} failed: {}", url, e.getMessage()); throw new ExternalServiceException("GET request failed", e); } } public <T> T post(String path, Object request, Class<T> responseType) { return post(path, request, responseType, Collections.emptyMap()); } public <T> T post(String path, Object request, Class<T> responseType, Map<String, String> headers) { String url = baseUrl + path; HttpHeaders httpHeaders = new HttpHeaders(); headers.forEach(httpHeaders::set); httpHeaders.setContentType(MediaType.APPLICATION_JSON); String requestBody; try { requestBody = objectMapper.writeValueAsString(request); } catch (Exception e) { throw new ExternalServiceException("Failed to serialize request", e); } HttpEntity<String> entity = new HttpEntity<>(requestBody, httpHeaders); try { ResponseEntity<T> response = restTemplate.exchange( url, HttpMethod.POST, entity, responseType); logger.debug("POST {} -> {}", url, response.getStatusCode()); return response.getBody(); } catch (Exception e) { logger.error("POST {} failed: {}", url, e.getMessage()); throw new ExternalServiceException("POST request failed", e); } } public <T> T put(String path, Object request, Class<T> responseType) { String url = baseUrl + path; HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); String requestBody; try { requestBody = objectMapper.writeValueAsString(request); } catch (Exception e) { throw new ExternalServiceException("Failed to serialize request", e); } HttpEntity<String> entity = new HttpEntity<>(requestBody, httpHeaders); try { ResponseEntity<T> response = restTemplate.exchange( url, HttpMethod.PUT, entity, responseType); logger.debug("PUT {} -> {}", url, response.getStatusCode()); return response.getBody(); } catch (Exception e) { logger.error("PUT {} failed: {}", url, e.getMessage()); throw new ExternalServiceException("PUT request failed", e); } } public void delete(String path) { String url = baseUrl + path; try { restTemplate.delete(url); logger.debug("DELETE {} -> Success", url); } catch (Exception e) { logger.error("DELETE {} failed: {}", url, e.getMessage()); throw new ExternalServiceException("DELETE request failed", e); } } public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; } public static class ExternalServiceException extends RuntimeException { public ExternalServiceException(String message) { super(message); } public ExternalServiceException(String message, Throwable cause) { super(message, cause); } } }

5. JUnit 5 Test Examples

// UserServiceHoverflyTest.java package com.example.hoverfly.test; import com.example.hoverfly.client.ExternalServiceClient; import com.example.hoverfly.service.HoverflyService; import io.specto.hoverfly.junit5.HoverflyExtension; import io.specto.hoverfly.junit5.api.HoverflyConfig; import io.specto.hoverfly.junit5.api.HoverflyCore; import io.specto.hoverfly.junit5.api.HoverflySimulate; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpStatus; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest @ExtendWith(HoverflyExtension.class) @HoverflyCore(config = @HoverflyConfig(adminPort = 8888, proxyPort = 8500)) class UserServiceHoverflyTest { @Autowired private ExternalServiceClient externalServiceClient; @Autowired private HoverflyService hoverflyService; @Autowired private ObjectMapper objectMapper; @BeforeEach void setUp() { // Point client to Hoverfly proxy externalServiceClient.setBaseUrl("http://localhost:8500"); } @Test @HoverflySimulate(source = @HoverflySimulate.Source(value = "classpath:hoverfly/user-service.json", type = HoverflySimulate.SourceType.FILE)) void testGetUserSuccess() { // When Map<String, Object> user = externalServiceClient.get("/users/1", HashMap.class); // Then assertNotNull(user); assertEquals(1, user.get("id")); assertEquals("John Doe", user.get("name")); assertEquals("[email protected]", user.get("email")); } @Test void testGetUserWithProgrammaticSimulation() { // Given String userJson = """ { "id": 123, "name": "Jane Smith", "email": "[email protected]", "active": true } """; hoverflyService.simulateGetRequest("jsonplaceholder.typicode.com", "/users/123", 200, userJson); // When Map<String, Object> user = externalServiceClient.get("/users/123", HashMap.class); // Then assertNotNull(user); assertEquals(123, user.get("id")); assertEquals("Jane Smith", user.get("name")); assertTrue((Boolean) user.get("active")); } @Test void testCreateUser() { // Given String requestBody = """ { "name": "New User", "email": "[email protected]" } """; String responseBody = """ { "id": 999, "name": "New User", "email": "[email protected]", "createdAt": "2024-01-15T10:30:00Z" } """; Map<String, String> headers = new HashMap<>(); headers.put("Location", "http://localhost:8500/users/999"); hoverflyService.simulatePostRequest("jsonplaceholder.typicode.com", "/users", requestBody, 201, responseBody, headers); Map<String, Object> userRequest = new HashMap<>(); userRequest.put("name", "New User"); userRequest.put("email", "[email protected]"); // When Map<String, Object> createdUser = externalServiceClient.post( "/users", userRequest, HashMap.class); // Then assertNotNull(createdUser); assertEquals(999, createdUser.get("id")); assertEquals("New User", createdUser.get("name")); } @Test void testGetUserNotFound() { // Given hoverflyService.simulateErrorScenario("jsonplaceholder.typicode.com", "/users/999", "GET", 404, "User not found"); // When & Then ExternalServiceClient.ExternalServiceException exception = assertThrows(ExternalServiceClient.ExternalServiceException.class, () -> externalServiceClient.get("/users/999", HashMap.class)); assertTrue(exception.getMessage().contains("GET request failed")); } @Test void testServiceUnavailable() { // Given hoverflyService.simulateErrorScenario("jsonplaceholder.typicode.com", "/users", "GET", 503, "Service temporarily unavailable"); // When & Then ExternalServiceClient.ExternalServiceException exception = assertThrows(ExternalServiceClient.ExternalServiceException.class, () -> externalServiceClient.get("/users", HashMap.class)); assertTrue(exception.getMessage().contains("GET request failed")); } @Test void testSlowResponse() { // Given - Simulate a slow response with 2 second delay String usersJson = """ [ {"id": 1, "name": "User 1"}, {"id": 2, "name": "User 2"} ] """; hoverflyService.simulateWithDelay("jsonplaceholder.typicode.com", "/users", "GET", 200, usersJson, 2000); long startTime = System.currentTimeMillis(); // When Object users = externalServiceClient.get("/users", Object.class); long endTime = System.currentTimeMillis(); // Then assertNotNull(users); long duration = endTime - startTime; assertTrue(duration >= 2000, "Response should take at least 2 seconds"); } }

6. Simulation Source Files

User Service Simulation

{ "data": { "pairs": [ { "request": { "path": [ { "matcher": "exact", "value": "/users/1" } ], "method": [ { "matcher": "exact", "value": "GET" } ], "destination": [ { "matcher": "exact", "value": "jsonplaceholder.typicode.com" } ], "scheme": [ { "matcher": "exact", "value": "http" } ] }, "response": { "status": 200, "body": "{\n \"id\": 1,\n \"name\": \"John Doe\",\n \"username\": \"johndoe\",\n \"email\": \"[email protected]\",\n \"address\": {\n \"street\": \"123 Main St\",\n \"city\": \"Springfield\",\n \"zipcode\": \"12345\"\n },\n \"phone\": \"1-555-123-4567\",\n \"website\": \"johndoe.com\"\n}", "encodedBody": false, "headers": { "Content-Type": [ "application/json" ], "Date": [ "Mon, 15 Jan 2024 10:00:00 GMT" ] } } }, { "request": { "path": [ { "matcher": "exact", "value": "/users" } ], "method": [ { "matcher": "exact", "value": "POST" } ], "destination": [ { "matcher": "exact", "value": "jsonplaceholder.typicode.com" } ], "scheme": [ { "matcher": "exact", "value": "http" } ], "body": [ { "matcher": "json", "value": "{\"name\":\"New User\",\"email\":\"[email protected]\"}" } ] }, "response": { "status": 201, "body": "{\n \"id\": 999,\n \"name\": \"New User\",\n \"email\": \"[email protected]\",\n \"createdAt\": \"2024-01-15T10:30:00Z\"\n}", "encodedBody": false, "headers": { "Content-Type": [ "application/json" ], "Location": [ "http://localhost:8500/users/999" ] } } } ], "globalActions": { "delays": [] } }, "meta": { "schemaVersion": "v5", "hoverflyVersion": "v1.0.0", "timeExported": "2024-01-15T10:00:00Z" } }

7. Advanced Hoverfly Features

Stateful Simulation Service

// StatefulSimulationService.java package com.example.hoverfly.service; import io.specto.hoverfly.junit.core.Hoverfly; import io.specto.hoverfly.junit.core.SimulationSource; import io.specto.hoverfly.junit.core.model.RequestFieldMatcher; import io.specto.hoverfly.junit.core.model.ResponseBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @Service public class StatefulSimulationService { private final Hoverfly hoverfly; private final Map<String, AtomicInteger> requestCounters = new HashMap<>(); private final Map<String, String> sessionStates = new HashMap<>(); @Autowired public StatefulSimulationService(Hoverfly hoverfly) { this.hoverfly = hoverfly; } public void simulateRateLimitedEndpoint(String service, String path, int limit, int windowSeconds) { String counterKey = service + path; requestCounters.putIfAbsent(counterKey, new AtomicInteger(0)); // This is a simplified implementation // In production, you'd want more sophisticated rate limiting simulation hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .get(path) .willReturn(ResponseBuilder.response() .status(200) .body("{\"message\": \"Success\"}") .build()), SimulationSource.service(service) .get(path) .willReturn(ResponseBuilder.response() .status(429) .body("{\"error\": \"Rate limit exceeded\", \"retryAfter\": \"60\"}") .build()) .withWeight(10) // Higher weight for success cases )); } public void simulateSessionBasedBehavior(String service, String basePath) { // Initial state - not authenticated hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .get(basePath + "/profile") .willReturn(ResponseBuilder.response() .status(401) .body("{\"error\": \"Not authenticated\"}") .build()) )); // Login endpoint hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .post(basePath + "/login") .body(RequestFieldMatcher.newJsonPartialMatcher("{\"username\":")) .willReturn(ResponseBuilder.response() .status(200) .body("{\"token\": \"abc123\", \"expiresIn\": 3600}") .header("Set-Cookie", "session=abc123; HttpOnly") .build()) )); // Authenticated profile access hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .get(basePath + "/profile") .header("Authorization", RequestFieldMatcher.newGlobMatcher("Bearer *")) .willReturn(ResponseBuilder.response() .status(200) .body("{\"username\": \"testuser\", \"email\": \"[email protected]\"}") .build()) )); } public void simulateCircuitBreakerScenario(String service, String path) { // Simulate a service that fails initially but recovers hoverfly.simulate(SimulationSource.dsl( SimulationSource.service(service) .get(path) .willReturn(ResponseBuilder.response() .status(503) .body("{\"error\": \"Service unavailable\"}") .build()), SimulationSource.service(service) .get(path) .willReturn(ResponseBuilder.response() .status(200) .body("{\"status\": \"healthy\"}") .build()) .withWeight(3) // Recovery case has lower weight initially )); } }

8. Integration Test Configuration

// TestConfig.java package com.example.hoverfly.config; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; import java.time.Duration; @TestConfiguration public class TestConfig { @Bean public RestTemplate testRestTemplate() { return new RestTemplate(); } @Bean public HoverflySimulationManager hoverflySimulationManager() { return new HoverflySimulationManager(); } } // HoverflySimulationManager.java package com.example.hoverfly.config; import io.specto.hoverfly.junit.core.Hoverfly; import io.specto.hoverfly.junit.core.HoverflyConfig; import io.specto.hoverfly.junit.core.HoverflyMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.annotation.PreDestroy; @Component public class HoverflySimulationManager { private static final Logger logger = LoggerFactory.getLogger(HoverflySimulationManager.class); private Hoverfly hoverfly; public void startSimulation() { if (hoverfly == null) { HoverflyConfig config = HoverflyConfig.localConfigs() .adminPort(8888) .proxyPort(8500) .captureHeaders("Content-Type", "Authorization", "X-Request-ID") .plainHttpTunneling(); hoverfly = new Hoverfly(config, HoverflyMode.SIMULATE); hoverfly.start(); logger.info("Hoverfly simulation started"); } } public void startCapture() { if (hoverfly == null) { HoverflyConfig config = HoverflyConfig.localConfigs() .adminPort(8888) .proxyPort(8500) .captureHeaders("Content-Type", "Authorization", "X-Correlation-ID") .destination("api.github.com") .destination("jsonplaceholder.typicode.com") .plainHttpTunneling(); hoverfly = new Hoverfly(config, HoverflyMode.CAPTURE); hoverfly.start(); logger.info("Hoverfly capture mode started"); } } @PreDestroy public void stop() { if (hoverfly != null && hoverfly.isRunning()) { hoverfly.close(); logger.info("Hoverfly stopped"); } } public Hoverfly getHoverfly() { return hoverfly; } }

This comprehensive Hoverfly implementation provides:

  • Service virtualization for external dependencies
  • Programmatic simulation configuration
  • Stateful behavior simulation
  • Error scenario testing
  • Performance testing with delays
  • JUnit 5 integration
  • Spring Boot compatibility
  • Simulation management and export capabilities

The setup allows you to test your Java applications reliably without depending on external services, enabling faster development cycles and more comprehensive testing.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper