本文目录导读:

- 基于接口的静态模拟(最简单)
- 使用 Mockito 框架动态模拟
- 使用 WireMock 模拟 HTTP 服务
- 使用 Testcontainers + MockServer
- 使用 Spring Cloud Contract(契约测试)
- Spring Boot 的 @Profile + @Conditional
- 使用配置化模拟
- 总结对比
在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契约验证。
流程:
- 定义契约(Groovy/YAML)
- 自动生成Stub JAR
- 消费者使用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
- 集成测试:使用 WireMock 或 MockServer
- 多环境切换:使用 Profile + 配置化模拟
- 微服务契约:使用 Spring Cloud Contract