跳到主要内容

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();
}

🏗️ 架构设计

核心组件:

  1. Mock类:核心Mock实现,无拦截器
  2. 代码生成器:动态生成Mock代理类

工作流程:

  1. 用户创建Mock:mock_object("service/path")
  2. 配置行为:mock.setup("method").returns(value)
  3. 激活Mock:mock.activate() → 生成代理类并编译
  4. 获取代理:mock.of() → 返回代理对象实例
  5. 直接调用:proxy.method() → 执行Mock行为
  6. 验证调用:moq.verify(proxy, times.once(), "method")
  7. 清理Mock:mock.deactivate() → 清理生成的文件

📦 安装配置

无需配置,开箱即用。