如何为Objective-C的反射和performSelector编写单元测试并mock返回值
针对这段Objective-C代码的val返回值Mock方案
你这段代码通过运行时获取A类,调用其类方法someMethod得到val,mock的核心是替换A类的someMethod类方法实现,让它返回你指定的值,常用有两种实现方案:
方案1:使用OCMock框架(推荐,单元测试通用方案)
OCMock原生支持类方法的存根(stub),不需要手动修改业务代码,示例代码如下:
#import <OCMock/OCMock.h> - (void)test_myMethod_mockVal { // 1. 构造你期望返回的mock值,可根据业务需求替换为任意类型的实例 id expectedMockVal = @"你要的mock返回值"; // 2. 对A类的someMethod类方法做存根,指定返回值 Class classA = NSClassFromString(@"A"); id mockA = OCMClassMock(classA); OCMStub(ClassMethod([mockA someMethod])).andReturn(expectedMockVal); // 如果someMethod返回基础类型(比如int、BOOL),改用andReturnValue: // OCMStub(ClassMethod([mockA someMethod])).andReturnValue(@(YES)); // 3. 调用待测试的myMethod,此时方法内部拿到的val就是你指定的expectedMockVal [yourTestInstance myMethod]; // 这里加你的业务断言逻辑 // 4. 测试结束停止mock,避免影响其他用例 [mockA stopMocking]; }
方案2:手动使用Runtime交换方法实现(无第三方依赖)
如果不想引入测试框架,可以直接用Runtime的方法交换能力替换实现,示例如下:
#import <objc/runtime.h> // 全局变量存储mock返回值 static id g_mockSomeMethodReturnVal = nil; // 自定义替换用的方法实现,和原someMethod的返回值、参数保持一致 + (id)mock_customSomeMethod { return g_mockSomeMethodReturnVal; } - (void)test_myMethod_mockVal { // 1. 构造期望的mock返回值 g_mockSomeMethodReturnVal = @"你要的mock返回值"; // 2. 获取原方法和替换方法的指针 Class classA = NSClassFromString(@"A"); Method originalMethod = class_getClassMethod(classA, @selector(someMethod)); Method swizzledMethod = class_getClassMethod([self class], @selector(mock_customSomeMethod)); // 3. 交换两个方法的实现 method_exchangeImplementations(originalMethod, swizzledMethod); // 4. 调用待测试方法 [yourTestInstance myMethod]; // 这里加你的业务断言逻辑 // 5. 测试结束把方法交换回来,避免污染其他测试 method_exchangeImplementations(originalMethod, swizzledMethod); g_mockSomeMethodReturnVal = nil; }
注意事项
- 如果
someMethod带参数,只要在stub或者替换方法时,对应匹配参数即可,逻辑一致。 - 优先使用OCMock这类成熟测试框架,可避免手动方法交换出现的内存泄露、方法污染问题。
内容的提问来源于stack exchange,提问作者Kai Zhang
相关产品推荐
相关产品推荐

