moq
专为GS语言设计的Mock测试库,采用源代码生成方案,完全兼容原生调用
🚀 快速开始
基础用法
import pkg.moq.*;
// 创建Mock
Mock mock = create_mock("game/user_service");
mock.setup("get_user_name")
.with_args([it.a_int()])
.returns("测试玩家");
// 激活Mock
mock.activate();
// 获取Mock代理对象
object ob = mock.of();
// 直接调用Mock方法(完全兼容原生调用)
string name = ob.get_user_name(123); // 直接调用
// 验证调用 - 使用全局验证函数
moq.verify(ob, times.once(), "get_user_name");
// 验证调用并检查参数
moq.verify(ob, times.once(), "get_user_name", 123);
// 注销Mock
mock.deactivate();
📖 核心功能
🔧 严格模式支持
GS-Moq 支持严格模式,用于验证被Mock的方法是否真实存在:
// 启用严格模式(默认)
Mock mock = mock_object("service/calculator");
mock.set_strict_mode(true); // 验证方法存在性
// 禁用严格模式(允许Mock不存在的方法)
mock.set_strict_mode(false);
// 检查严格模式状态(默认启用)
bool is_strict = mock.is_strict_mode();
📊 验证结果详情
Mock验证返回详细的结果信息:
VerifyResult result = moq.verify(mock_proxy, times.once(), "method_name");
if (result.is_success()) {
printf("✅ %s\n", result.get_message());
} else {
printf("❌ %s\n", result.get_message());
}
🎯 参数匹配器
GS-Moq 提供丰富的参数匹配器,利用GS内置类型检查:
// 基础类型匹配
it.a_string() // 字符串类型
it.a_int() // 整数类型
it.a_float() // 浮点数类型
it.a_bool() // 布尔类型
it.a_array() // 数组类型
it.a_map() // 映射类型
it.a_object() // 对象类型
// 值匹配
it.any() // 任意值
it.equals(value) // 精确匹配
// 条件匹配
it.in_range(1, 100) // 数值范围
it.string_contains("admin") // 字符串包含
it.string_starts_with("user_") // 字符串前缀
it.string_ends_with(".log") // 字符串后缀
it.not_nil() // 非空检查
it.greater_than(0) // 大于某值
it.less_than(100) // 小于某值
// 集合匹配
it.array_size(3) // 数组大小
it.empty_array() // 空数组
it.empty_map() // 空映射
it.map_has_key("id") // 映射包含键
// 高级匹配
it.regex("^user_\\d+$") // 正则表达式
it.matches(custom_func, "自定义条件") // 自定义匹配
📏 调用次数约束
Times类提供多种调用次数约束:
// 精确次数
times.never() // 从不调用
times.once() // 调用一次
times.twice() // 调用两次
times.exactly(5) // 正好5次
// 范围约束
times.at_least(2) // 至少2次
times.at_most(3) // 最多3次
times.between(1, 5) // 1到5次
// 常用别名
times.at_least_once() // 至少一次
times.any() // 任意次数(无约束)
Mock 类 - 直接替换方案
基础用法:
// 创建对象Mock
Mock mock = mock_object("path/to/service");
// 设置行为
mock.setup("method_name")
.with_args([it.a_string()])
.returns("result");
// 设置异常抛出
mock.setup("connect_database")
.throws("数据库连接失败");
// 设置回调函数返回值
mock.setup("calculate")
.with_args([it.a_int(), it.a_int()])
.returns_call((array args) {
int a = args[0];
int b = args[1];
return a + b;
});
mock.activate();
// 获取Mock代理对象(用于直接调用)
object ob = mock.of();
// 直接调用Mock对象
mixed result = ob.method_name(args);
// 验证调用
moq.verify(ob, times.once(), "method_name");
// 清理
mock.deactivate();
🎭 高级Setup功能
throws - 异常抛出
// 模拟方法抛出异常
mock.setup("connect_database")
.throws("数据库连接失败");
// 带参数匹配的异常抛出
mock.setup("query_user")
.with_args([it.equals(404)])
.throws("用户ID %d 不存在", 404);
// 格式化异常消息
mock.setup("update_user")
.throws("更新用户 %s 失败:%s", user_name, "权限不足");
returns_call - 回调函数返回值
// 基础回调返回值
mock.setup("calculate")
.with_args([it.a_int(), it.a_int()])
.returns_call((array args) {
int a = args[0];
int b = args[1];
return a * b; // 动态计算结果
});
// 条件逻辑回调
mock.setup("get_user_info")
.returns_call((array args) {
int user_id = args[0];
if (user_id == 1) {
return ({ "name": "管理员", "role": "admin" });
}
return ({ "name": "普通用户", "role": "user" });
});
组合使用
// 正常情况返回计算结果
mock.setup("divide")
.with_args([it.a_int(), it.greater_than(0)])
.returns_call((array args) {
return args[0] / args[1];
});
// 除零异常
mock.setup("divide")
.with_args([it.a_int(), it.equals(0)])
.throws("除数不能为零");
🔧 Mock类型
对象Mock
// 创建对象的Mock代理
Mock obj_mock = create_mock("game/item");
// 或使用对象定义
import xxx.item;
Mock obj_mock = create_mock(item);
类Mock
// Mock类的构造和方法
class_map calculator_class = {...}; // 类定义映射
Mock class_mock = create_mock(calculator_class);
✅ 直接替换Mock方案
正确用法
// ✅ 正确:显式获取Mock代理对象
Mock mock = create_mock("game/user_service");
mock.activate();
object ob = mock.of(); // 获取代理对象
int level = ob.get_level(user_id); // 直接调用
🎯 设计原理
代码生成驱动:
// GS-Moq 通过代码生成创建Mock代理类
// 用户显式使用Mock代理对象,而非透明拦截
Mock mock = create_mock("service/calculator");
mock.setup("add")
.with_args([it.a_int(), it.a_int()])
.returns(10);
mock.activate();
object calculator = mock.of();
int result = calculator.add(2, 3); // 返回10
📝 完整测试流程
// 1. SETUP - 配置Mock行为
Mock mock = create_mock("service/path");
mock.setup("method").returns(result);
mock.activate();
// 2. ACT - 使用Mock代理对象
object ob = mock.of();
mixed actual = ob.method(args);
// 3. VERIFY - 验证Mock调用
moq.verify(ob, times.once(), "method");
// 4. CLEANUP - 清理Mock
mock.deactivate();
🔗 调用顺序验证 verify_sequence
用于验证跨对象、跨域的调用顺序,支持可选参数匹配:
// 准备
Mock db = create_mock("service/db").set_strict_mode(false);
Mock logger = create_mock("service/logger").set_strict_mode(false);
db.setup("save_user").with_args([it.a_string(), it.map_has_key("name")]).returns(true);
logger.setup("log_info").with_args([it.a_string()]).returns(nil);
db.activate();
logger.activate();
object dbo = db.of();
object lgo = logger.of();
// 业务调用
dbo.save_user("u1", {"name": "n"});
lgo.log_info("saved");
// 验证顺序:先save_user,再log_info
VerifyResult seq = moq.verify_sequence([
[dbo, "save_user", "u1", it.map_has_key("name")],
[lgo, "log_info"]
]);
printf("%s\n", seq.get_message());
🧪 综合示例
多服务协作测试
void test_user_registration()
{
// 创建专用域用于隔离测试
domain email_domain = domain.create("email_domain", false, 0);
domain db_domain = domain.create("db_domain", false, 0);
defer {
domain.delete(email_domain);
domain.delete(db_domain);
};
// 创建多个Mock服务,使用不同的域策略
Mock email_service = create_mock("service/email");
Mock db_service = create_mock("service/database");
// 设置Mock行为
email_service.setup("send_welcome_email")
.with_args([it.a_string()])
.returns(true);
db_service.setup("save_user")
.with_args([it.a_map()])
.returns({"id": 12345});
// 激活Mock
email_service.activate();
db_service.activate();
// 获取Mock代理对象
object mock_email = email_service.of(true, email_domain);
object mock_db = db_service.of(true, db_domain);
// 注入到被测试系统
user_service.set_email_service(mock_email);
user_service.set_db_service(mock_db);
// 执行测试
map result = user_service.register_user("test@example.com", "password");
// 验证调用
moq.verify(mock_email, times.once(), "send_welcome_email");
moq.verify(mock_db, times.once(), "save_user");
// 清理
email_service.deactivate();
db_service.deactivate();
}
类Mock测试
void test_calculator_mock()
{
// 定义类映射
class_map calculator_class = {...}; // 具体的类定义
Mock calculator_mock = create_mock(calculator_class);
calculator_mock.setup("add")
.with_args([it.a_int(), it.a_int()])
.returns(10);
calculator_mock.activate();
// 使用Mock类
object calculator = calculator_mock.of();
int result = calculator.add(2, 3); // 返回10
moq.verify(calculator, times.once(), "add");
calculator_mock.deactivate();
}