【软件工程】单元测试之Mock技术详解:C++与Java实现
Mock技术是单元测试中的核心工具,用于模拟外部依赖对象的行为,使测试专注于被测对象的逻辑验证。
·
文章目录
Mock技术详解:C++与Java实现
Mock技术是单元测试中的核心工具,用于模拟外部依赖对象的行为,使测试专注于被测对象的逻辑验证。
一、Mock技术核心原理
1. 三大核心功能
功能 | 作用 | 实现机制 |
---|---|---|
行为模拟 | 预设方法的返回值/异常 | Stubbing (打桩) |
调用验证 | 检查方法是否按预期调用 | 调用计数和参数记录 |
交互控制 | 验证方法调用顺序和依赖关系 | 调用序列跟踪 |
2. 工作原理图解
[被测对象] → [Mock对象]
↓ ↓
(执行逻辑) (预设行为)
↓ ↓
[验证结果] ← [调用记录]
二、Java实现详解(Mockito框架)
1. 完整测试场景
// 依赖接口
interface PaymentGateway {
boolean pay(BigDecimal amount, String currency);
String getTransactionId();
}
// 被测服务
class OrderService {
private final PaymentGateway gateway;
public OrderService(PaymentGateway gateway) {
this.gateway = gateway;
}
public String processOrder(Order order) {
if (gateway.pay(order.getAmount(), "USD")) {
return gateway.getTransactionId();
}
throw new PaymentFailedException();
}
}
// 测试类
public class OrderServiceTest {
@Test
public void testSuccessfulPayment() {
// 1. 创建Mock对象
PaymentGateway mockGateway = mock(PaymentGateway.class);
// 2. 行为模拟 (Stubbing)
when(mockGateway.pay(any(BigDecimal.class), eq("USD")))
.thenReturn(true);
when(mockGateway.getTransactionId())
.thenReturn("TX-123456");
// 3. 注入依赖并执行
OrderService service = new OrderService(mockGateway);
String txId = service.processOrder(new Order(BigDecimal.valueOf(100)));
// 4. 结果断言
assertEquals("TX-123456", txId);
// 5. 行为验证
verify(mockGateway, times(1)).pay(BigDecimal.valueOf(100), "USD");
verify(mockGateway, never()).refund(any()); // 验证未调用
}
@Test
public void testFailedPayment() {
PaymentGateway mockGateway = mock(PaymentGateway.class);
when(mockGateway.pay(any(), anyString())).thenReturn(false);
OrderService service = new OrderService(mockGateway);
assertThrows(PaymentFailedException.class, () -> {
service.processOrder(new Order(BigDecimal.valueOf(50)));
});
}
}
2. 高级特性应用
// 1. 验证调用顺序
InOrder inOrder = inOrder(mockGateway);
inOrder.verify(mockGateway).connect();
inOrder.verify(mockGateway).pay(any(), anyString());
// 2. 模拟异常抛出
when(mockGateway.getTransactionId())
.thenThrow(new NetworkException("Timeout"));
// 3. 参数捕获
ArgumentCaptor<BigDecimal> amountCaptor = ArgumentCaptor.forClass(BigDecimal.class);
verify(mockGateway).pay(amountCaptor.capture(), anyString());
assertEquals(100, amountCaptor.getValue().intValue());
// 4. 动态返回值
when(mockGateway.getTransactionId())
.thenAnswer(invocation -> "TX-" + System.currentTimeMillis());
三、C++实现详解(Google Mock框架)
1. 完整测试场景
// 依赖接口
class Sensor {
public:
virtual ~Sensor() = default;
virtual double readTemperature() = 0;
virtual bool calibrate() = 0;
};
// 被测控制器
class TemperatureController {
public:
explicit TemperatureController(Sensor* sensor) : sensor_(sensor) {}
bool checkOverheating() {
return sensor_->readTemperature() > 100.0;
}
void emergencyShutdown() {
if (checkOverheating()) {
sensor_->calibrate();
// 关闭系统逻辑...
}
}
private:
Sensor* sensor_;
};
// Mock实现
#include <gmock/gmock.h>
class MockSensor : public Sensor {
public:
MOCK_METHOD(double, readTemperature, (), (override));
MOCK_METHOD(bool, calibrate, (), (override));
};
// 测试用例
#include <gtest/gtest.h>
TEST(TemperatureControllerTest, TriggersShutdownWhenOverheating) {
// 1. 创建Mock对象
MockSensor mockSensor;
// 2. 行为模拟
EXPECT_CALL(mockSensor, readTemperature())
.WillOnce(Return(150.0)); // 模拟高温
EXPECT_CALL(mockSensor, calibrate())
.WillOnce(Return(true));
// 3. 执行测试
TemperatureController controller(&mockSensor);
controller.emergencyShutdown();
// 4. Google Mock自动验证所有EXPECT_CALL
}
TEST(TemperatureControllerTest, NoActionWhenNormalTemp) {
MockSensor mockSensor;
EXPECT_CALL(mockSensor, readTemperature())
.WillRepeatedly(Return(80.0)); // 正常温度
EXPECT_CALL(mockSensor, calibrate()).Times(0); // 禁止调用
TemperatureController controller(&mockSensor);
controller.emergencyShutdown();
}
2. 高级特性应用
// 1. 复杂参数匹配
EXPECT_CALL(mockDb, executeQuery(StartsWith("SELECT"), Contains("id=123")));
// 2. 调用序列控制
Sequence s1, s2;
EXPECT_CALL(mockComm, connect()).InSequence(s1);
EXPECT_CALL(mockComm, send(_, _)).InSequence(s1, s2);
EXPECT_CALL(mockComm, receive()).InSequence(s2);
// 3. 自定义动作
EXPECT_CALL(mockAlarm, trigger())
.WillOnce(Invoke([](){
std::cout << "Alarm triggered in test!";
return true;
}));
// 4. 设置期望值范围
EXPECT_CALL(mockSensor, readTemperature())
.WillOnce(Return(95.0))
.WillOnce(Return(105.0)); // 多次调用不同返回值
四、Mock技术最佳实践
-
选择正确的模拟对象
- 只模拟跨边界依赖(数据库、网络、文件系统)
- 避免模拟值对象和业务逻辑对象
-
验证粒度控制
// 正确:验证核心交互 verify(mockGateway).pay(any()); // 错误:过度验证实现细节 verify(mockGateway, times(1)).generateLogEntry(); // 暴露内部实现
-
保持测试独立性
- 每个测试前重置Mock状态
- 避免Mock对象在测试间共享
-
命名规范
// 好的命名 MockDatabase mockDatabase; EXPECT_CALL(mockDatabase, connect()); // 差的命名 MockA mockA; EXPECT_CALL(mockA, func1());
-
处理遗留代码
- 对无接口的类使用
PowerMock
(Java) - 对C++非虚函数使用
Seam
技术
// 通过模板实现非侵入式Mock template <typename TSensor> class TemperatureController { public: explicit TemperatureController(TSensor& sensor) : sensor_(sensor) {} private: TSensor& sensor_; };
- 对无接口的类使用
五、常见问题解决方案
问题类型 | 解决方案 | 代码示例 |
---|---|---|
模拟静态方法 | 使用PowerMock (Java) | @PrepareForTest(StaticClass.class) |
模拟构造函数 | 使用Mockito-inline (Java) | withSettings().useConstructor().defaultAnswer() |
模拟final类 | Mockito 4.0+支持 | mock(FinalClass.class) |
验证异步调用 | 结合CountDownLatch (Java) | verify(mock, timeout(1000)).method() |
模拟系统时间 | 封装时间服务 | mockClock.getTime() |
六、技术选型建议
指标 | Java推荐 | C++推荐 | 原因 |
---|---|---|---|
易用性 | Mockito | HippoMocks | 简洁的API设计 |
功能完整性 | PowerMock | Google Mock | 支持静态方法/构造函数模拟 |
集成度 | Spring Test | Google Test | 框架原生支持 |
性能 | Mockito | FakeIt | 轻量级无代码生成 |
多语言支持 | JMockit | CPPUnit+Mock | 支持多种模拟方案 |
黄金法则:优先选择满足需求的轻量级框架,Mockito和Google Mock能满足90%的场景需求。
更多推荐
所有评论(0)