Leem.fin
Leem.fin

Reputation: 42582

why I can access private method in unit test class in my case

Say I have a private method -(void) doSomething defined in m file of School class (it is not declared in interface header):

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

I have a unit test case for School class:

@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

I know I normally can't access this private method. But if I re-declare the -(void) doSomething in my test class as below:

@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

Why in above way I can access private method of School class with mySchool instance? What is the objective-c theory behind this?

(I am doing this because I have read the answer from this question, but I don't understand why we can do it? what is the theory behind?)

Upvotes: 1

Views: 407

Answers (2)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726479

Objective-C lacks protection mechanism for methods and fields that would be active at run time. All "protection" is done at compile time, when you hide the method in .m file to make it "private".

Once the method is compiled, its information is stored in a table that is used to dispatch method invocations at runtime. Anyone who has the selector for your method can invoke it, as if it were public. This enables a lot of dynamic behavior in Objective-C.

Unfortunately, there is no protection of selectors at runtime. When you re-declare the private method for testing, you are essentially telling Objective-C that you know that the method is there, and insist on letting you make a call.

Two things could happen when you do this: if the method is actually there, the call is going to succeed. If the method is not there, however, you would end up with a crash on calling an invalid selector.

the method is re-delclared in my test class not in School class

This part is slightly tricky. id is a special type, which allows you to call any method with very little compile-time checking. Basically, the compiler verifies that there is any type that it knows that has a method with the matching signature, and then it lets you make the call. This is similar to creating a @SELECTOR for your method, and then calling it dynamically on an id-typed object. Essentially, the compiler verifiers that you didn't make an obvious typo, and lets the call to proceed.

Upvotes: 1

Losiowaty
Losiowaty

Reputation: 8006

It is worth noting that you didn't "redeclare" the method - you added a doSomething method to SchoolTest class. This is much different from what was mentioned in the linked answer - there a category was used to show "private" methods to the test class. So something like this would be better :

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

What you did worked, because mySchoolMock is of id type, and doSomething was a visible symbol, so Xcode and compilers didn't warn you about anything, and it worked because mechanisms explained by @dasblinkenlight did their job at runtime.

Upvotes: 0

Related Questions