Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing private methods from a category?

I have a category on NSString class that contains a private helper method. It would be handy if I could use this method in my unit test. However I have difficulties to expose it. When I create a class extension on NSString and declare the method here, the method is not visible in unit test. And it doesn't matter if I create the class extension in a separate header file, or as a part of unit test .m file.

It looks like I am missing something here.

Any help guys?

like image 750
Earl Grey Avatar asked Jan 22 '14 17:01

Earl Grey


People also ask

Can we write unit test for private methods?

Why We Shouldn't Test Private Methods. As a rule, the unit tests we write should only check our public methods contracts. Private methods are implementation details that the callers of our public methods aren't aware of. Furthermore, changing our implementation details shouldn't lead us to change our tests.

How would you unit test private methods?

To test private methods, you just need to test the public methods that call them. Call your public method and make assertions about the result or the state of the object. If the tests pass, you know your private methods are working correctly.

Can private methods be accessed from another class?

We can call the private method of a class from another class in Java (which are defined using the private access modifier in Java). We can do this by changing the runtime behavior of the class by using some predefined methods of Java. For accessing private method of different class we will use Reflection API.


2 Answers

Common unit testing guidance would tell you not to try and test your private methods. Only test via your public interfaces. Private methods are simply an implementation detail that could change at any time, when you refactor. Your public interfaces should be pretty stable, and will exercise your private methods.

However, if you still want to test your private category methods, the following works for me...

First, your category:

UIImage+Example.h

@interface UIImage (Example)    
@end

UIImage+Example.m

@implementation UIImage (Example)

+ (NSString *)examplePrivateMethod
{
    return @"Testing";
}

@end

MyExampleTests.m

#import <XCTest/XCTest.h>
#import "UIImage+Example.h"

@interface UIImage (Example_Test)
+ (NSString *)examplePrivateMethod;
@end

@interface MyExampleTests : XCTestCase
@end

@implementation MyExampleTests

- (void)testExample
{
    XCTAssertEqualObjects(@"Test", [UIImage examplePrivateMethod], @"Test should be test");
}

@end

Essentially, redeclare your private method in a new category in your test. However, as mentioned above this is exposing private methods just for the purpose of testing, and coupling your tests to your implementation.

like image 68
James Frost Avatar answered Oct 12 '22 20:10

James Frost


You can execute any method (private or not) on an object by simply using performSelector: on it, like so:

[something performSelector:@selector(somePrivateMethod)];

But I agree with James that you should only do that when absolutely necessary.

like image 45
Johannes Fahrenkrug Avatar answered Oct 12 '22 22:10

Johannes Fahrenkrug