为什么我可以在我的情况下访问单元测试类中的私有方法

时间:2016-05-24 10:57:00

标签: ios objective-c unit-testing

假设我在-(void) doSomething类的m文件中定义了一个私有方法School(它未在接口头中声明):

@implementation School
-(void) doSomething {
   ...
}
@end

我有School类的单元测试用例:

@interface SchoolTest : XCTestCase
@end

@implementation SchoolTest
 - (void)testExample {
   id mySchoolMock = [OCMockObject partialMockForObject:[School new]];
   // I know I can't access '-(void)doSomething' since it is a private method
 }
@end

我知道我通常无法访问此私有方法。但如果我在我的测试类中重新声明-(void) doSomething如下:

@interface SchoolTest : XCTestCase
// re-declare it in my test class
-(void) doSomething
@end

@implementation SchoolTest
 - (void)testExample {
   id mySchoolMock = [OCMockObject partialMockForObject:[School new]];
   // Now I can access '-(void)doSomething'!!! Why now I can access the private method in `mySchool` Instance in my test class ? 
  [mySchoolMock doSomething]; 
 }
@end

为什么以上述方式我可以使用School实例访问mySchool类的私有方法?这背后的客观理论是什么?

(我这样做是因为我已经阅读了this question的答案,但我不明白为什么我们能做到这一点?背后的理论是什么?)

2 个答案:

答案 0 :(得分:1)

Objective-C缺乏在运行时处于活动状态的方法和字段的保护机制。当您在.m文件中隐藏该方法以使其“私有”时,所有“保护”都在编译时完成。

编译方法后,其信息存储在一个表中,该表用于在运行时调度方法调用。拥有方法选择器的任何人都可以调用它,就好像它是公共的一样。这在Objective-C中实现了很多动态行为。

不幸的是,运行时没有选择器的保护。当您重新声明私有方法进行测试时,您实际上是在告诉Objective-C您知道该方法存在,并且坚持让您拨打电话。

执行此操作时可能会发生两件事:如果方法实际存在,则调用将成功。但是,如果该方法不存在,则最终会在调用无效选择器时崩溃。

  

该方法在我的测试类中重新声明,而不是在学校类

这部分有点棘手。 id是一种特殊类型,它允许您使用非常少的编译时检查来调用任何方法。基本上,编译器会验证是否存在任何知道具有匹配签名的方法的类型,然后它允许您进行调用。这类似于为您的方法创建@SELECTOR,然后在id类型的对象上动态调用它。基本上,编译器验证器没有明显错误,并让调用继续进行。

答案 1 :(得分:0)

值得注意的是,你没有重新宣布"方法 - 您向doSomething类添加了SchoolTest方法。这与链接答案中提到的内容大不相同 - 有一个类别用于显示"私人"测试类的方法。所以这样的事情会更好:

@interface School (Tests)
    - (void)doSomething;
@end

你做了什么,因为mySchoolMock属于id类型,而doSomething是一个可见的符号,所以Xcode和编译器并没有警告你任何事情,它因为@dasblinkenlight解释的机制在运行时完成了它们的工作。