Java案例如何实现服务仿真?

wen python案例 2

本文目录导读:

Java案例如何实现服务仿真?

  1. 基于接口的静态模拟(最简单)
  2. 使用 Mockito 框架动态模拟
  3. 使用 WireMock 模拟 HTTP 服务
  4. 使用 Testcontainers + MockServer
  5. 使用 Spring Cloud Contract(契约测试)
  6. Spring Boot 的 @Profile + @Conditional
  7. 使用配置化模拟
  8. 总结对比

在Java中实现服务仿真(Service Simulation/Stub)通常用于测试、开发阶段或模拟第三方接口不可用时的替代方案,以下是几种常见的实现思路和案例:


基于接口的静态模拟(最简单)

定义一个接口,然后创建它的模拟实现类。

示例:

// 定义服务接口
public interface PaymentService {
    String processPayment(String orderId, double amount);
}
// 模拟实现
@Component
public class PaymentServiceStub implements PaymentService {
    @Override
    public String processPayment(String orderId, double amount) {
        // 模拟延迟
        try { Thread.sleep(200); } catch (InterruptedException e) {}
        if (orderId.startsWith("FAIL")) {
            return "FAILED: Insufficient funds";
        }
        return "SUCCESS: Transaction " + orderId + " completed";
    }
}

使用:

@Service
public class OrderService {
    private final PaymentService paymentService;
    public OrderService(@Qualifier("paymentServiceStub") PaymentService paymentService) {
        this.paymentService = paymentService;
    }
    public void placeOrder() {
        String result = paymentService.processPayment("ORD123", 99.99);
        // 处理结果
    }
}

使用 Mockito 框架动态模拟

适合单元测试,可以灵活定义行为。

@Test
public void testOrderServiceWithMock() {
    // 创建 Mock
    PaymentService mockService = Mockito.mock(PaymentService.class);
    // 定义行为
    Mockito.when(mockService.processPayment("ORD001", 100.0))
           .thenReturn("SUCCESS");
    Mockito.when(mockService.processPayment("ORD002", 100.0))
           .thenThrow(new RuntimeException("Service unavailable"));
    // 注入并使用
    OrderService orderService = new OrderService(mockService);
    // 进行测试断言
}

使用 WireMock 模拟 HTTP 服务

适合模拟 REST API 等远程服务。

Maven 依赖:

<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock-jre8</artifactId>
    <version>2.35.0</version>
    <scope>test</scope>
</dependency>

示例代码:

@BeforeEach
public void setup() {
    WireMockServer wireMockServer = new WireMockServer(8089);
    wireMockServer.start();
    // 模拟一个 REST 端点
    stubFor(get(urlEqualTo("/api/payment/verify"))
            .withHeader("Authorization", equalTo("Bearer token123"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBody("{\"status\":\"approved\",\"transactionId\":\"TXN001\"}")
                .withFixedDelay(500)  // 模拟延迟
            ));
    // 模拟错误场景
    stubFor(post(urlEqualTo("/api/payment/charge"))
            .withRequestBody(containing("amount\":-1"))
            .willReturn(aResponse()
                .withStatus(400)
                .withBody("{\"error\":\"Invalid amount\"}")
            ));
}
@Test
public void testPaymentApi() {
    // 让待测服务调用 localhost:8089 的模拟服务
    RestTemplate restTemplate = new RestTemplate();
    ResponseEntity<String> response = restTemplate.exchange(
        "http://localhost:8089/api/payment/verify",
        HttpMethod.GET,
        new HttpEntity<>(createHeaders()),
        String.class
    );
    assertEquals(200, response.getStatusCodeValue());
}

使用 Testcontainers + MockServer

适合更复杂的模拟场景(如数据库、消息队列等)。

依赖:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mock-server</groupId>
    <artifactId>mockserver-netty</artifactId>
    <version>5.14.0</version>
    <scope>test</scope>
</dependency>

示例:

@Container
public static GenericContainer<?> mockServer = new GenericContainer<>("mockserver/mockserver:5.14.0")
    .withExposedPorts(1080);
@BeforeEach
public void setupMockExpectations() {
    MockServerClient client = new MockServerClient(
        mockServer.getHost(), 
        mockServer.getFirstMappedPort()
    );
    client.when(
        request()
            .withMethod("POST")
            .withPath("/api/v1/process")
            .withBody(json("{\"type\":\"order\"}"))
    ).respond(
        response()
            .withStatusCode(202)
            .withHeader("Location", "/api/v1/status/123")
            .withDelay(TimeUnit.MILLISECONDS, 100)
    );
}

使用 Spring Cloud Contract(契约测试)

适合微服务间的API契约验证。

流程:

  1. 定义契约(Groovy/YAML)
  2. 自动生成Stub JAR
  3. 消费者使用Stub进行测试

契约示例 (groovy):

Contract.make {
    description "模拟支付成功"
    request {
        method 'POST'
        url '/api/payment'
        headers {
            contentType('application/json')
        }
        body([
            orderId: $(anyUuid()),
            amount: $(anyDouble())
        ])
    }
    response {
        status 200
        headers {
            contentType('application/json')
        }
        body([
            transactionId: $(anyUuid()),
            status: 'SUCCESS'
        ])
    }
}

Spring Boot 的 @Profile + @Conditional

通过配置切换真实/模拟服务。

// 真实实现
@Component
@Profile("!stub")
public class RealPaymentService implements PaymentService {
    @Override
    public String processPayment(String orderId, double amount) {
        // 调用真实第三方API
    }
}
// 模拟实现
@Component
@Profile("stub")
public class StubPaymentService implements PaymentService {
    @Override
    public String processPayment(String orderId, double amount) {
        // 返回预设数据
    }
}

启动时指定:

java -jar app.jar --spring.profiles.active=stub

使用配置化模拟

从外部配置文件读取模拟响应,方便测试不同场景。

application-stub.yml:

stub:
  payment-service:
    delay-ms: 200
    responses:
      "ORD001": "SUCCESS: Order completed"
      "ORD002": "FAILED: Timeout"
      "ORD*": "SUCCESS: Default success"
@Component
@Profile("stub")
public class ConfigurableStubPaymentService implements PaymentService {
    @Value("${stub.payment-service.responses}")
    private Map<String, String> responses;
    @Value("${stub.payment-service.delay-ms:0}")
    private int delayMs;
    @Override
    public String processPayment(String orderId, double amount) {
        applyDelay();
        return responses.entrySet().stream()
            .filter(e -> orderId.matches(e.getKey().replace("*", ".*")))
            .findFirst()
            .map(Map.Entry::getValue)
            .orElse("SUCCESS: Default response");
    }
}

总结对比

方法 适合场景 复杂度 灵活性
静态Stub类 简单场景、快速验证
Mockito 单元测试
WireMock HTTP API模拟
MockServer 微服务、消息 中高
Spring Cloud Contract 契约测试
Profile切换 开发/测试环境隔离
配置化模拟 多场景测试

建议:

  • 单元测试:使用 Mockito + 静态Stub
  • 集成测试:使用 WireMockMockServer
  • 多环境切换:使用 Profile + 配置化模拟
  • 微服务契约:使用 Spring Cloud Contract

抱歉,评论功能暂时关闭!