use mocked object created in initializer - ios

I have a Worker class, its header:
#interface Worker : NSObject {
// instance variable `task`
MyTask *task;
}
#end
its initialiser method is like this:
#implementation Worker
-(id)initWithName:(NSString*)name{
self = [super init];
if (self) {
// I need to use mocked task in my test case
task = [[MyTask alloc] init];
...
...
}
return self
}
-(void)doWork{
[task start];
}
I want to unit test this class, I am using OCMock library to do the mock. I know I can create a mocked task by:
id mockedTask = OCMClassMock([MyTask class]);
But, how can I inject this mocked task into my Worker instance in test case ? I mean task is created in initialiser method, when I create a Worker instance in my test class:
-(void)setup{
// how to inject the mocked task in to this workerUnderTest
Worker *workerToTest = [[Worker alloc] initWithName:#"John"];
}
I need the mocked task to be used in test. How can I achieve it?
==== My Test case with #iSashok's answer (it doesn't work) ====
#interface WorkerTestCase : XCTestCase
#end
#implementation WorkerTestCase{
Worker *workerToTest;
id mockedTask;
}
- (void)setUp {
[super setUp];
workerToTest = [[Worker alloc] initWithName:#"John"];
mockedTask = OCMClassMock([MyTask class]);
OCMStub([workerToTest task]).andReturn(mockedTask);
}
-(void) testDoWork{
[workerToTest doWork];
// it fails. method is not invoked!
OCMVerify([mockedTask start]);
}
...
#end
As you see above, my test case fails, it complains that there is no invokation on mockedTask object. But when I set breakpoints to real class implementation, the [task start] is invoked, it indicates the mockedTask is not injected successfully.

You can inject it using OCMStub() like below and you need declare task ivar as a property
#interface Worker : NSObject
#property(nonatomic,strong)MyTask *task;
#end
//
id mockedTask = OCMClassMock([MyTask class]);
OCMStub([workerToTest task]).andReturn(mockedTask);
You method will look like
-(void)setup{
// how to inject the mocked task in to this workerUnderTest
Worker *workerToTest = [[Worker alloc] initWithName:#"John"];
id mockedTask = OCMClassMock([MyTask class]);
OCMStub([workerToTest task]).andReturn(mockedTask);
}

Related

Reset singleton instance to nil after each test case

I am using OCMock 3 to unit test my iOS project.
I use dispatch_once() created a singleton class MyManager :
#implementation MyManager
+ (id)sharedInstance {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
I have a method in School class which uses the above singleton:
#implementation School
...
- (void) createLecture {
MyManager *mgr = [MyManager sharedInstance];
[mgr checkLectures];
...
}
#end
Now, I want to unit test this method, I use a partial mock of MyManager:
- (void) testCreateLecture {
// create a partially mocked instance of MyManager
id partialMockMgr = [OCMockObject partialMockForObject:[MyManager sharedInstance]];
// run method to test
[schoolToTest createLecture];
...
}
- (void)tearDown {
// I want to set the singleton instance to nil, how to?
[super tearDown];
}
In tearDown phase, I want to set the singleton instance to nil so that the following test case could start from clean state.
I know on internet, some people suggest to move the static MyManager *sharedMyManager outside the +(id)sharedInstance method. But I would like to ask, is there any way to set the instance to nil without moving it outside +(id)sharedInstance method? (Any solution like java reflection?)
You can't achieve what you want with a local static variable. Block-scoped statics are only visible inside their lexical context.
We do this by making the singleton instance a static variable scoped to the class implementation and adding a mutator to override it. Generally that mutator is only called by tests.
#implementation MyManager
static MyManager *_sharedInstance = nil;
static dispatch_once_t once_token = 0;
+(instancetype)sharedInstance {
dispatch_once(&once_token, ^{
if (_sharedInstance == nil) {
_sharedInstance = [[MyManager alloc] init];
}
});
return _sharedInstance;
}
+(void)setSharedInstance:(MyManager *)instance {
once_token = 0; // resets the once_token so dispatch_once will run again
_sharedInstance = instance;
}
#end
Then in your unit test:
// we can replace it with a mock object
id mockManager = [OCMockObject mockForClass:[MyManager class]];
[MyManager setSharedInstance:mockManager];
// we can reset it so that it returns the actual MyManager
[MyManager setSharedInstance:nil];
This also works with partial mocks, as in your example:
id mockMyManager = [OCMockObject partialMockForObject:[MyManager sharedInstance]];
[[mockMyManager expect] checkLectures];
[MyManager setSharedInstance:mockMyManager];
[schoolToTest createLecture];
[mockMyManager verify];
[mockMyManager stopMocking];
// reset it so that it returns the actual MyManager
[MyManager setSharedInstance:nil];
Here's a full breakdown of the approach.
The answer is no, because you use dispatch_once(&onceToken, ^{ so even if you added another method which could reset the variable to nil you'd never be able to initialise it again.
So you already have one solution and the best solution is to not access the singleton directly (use dependency injection instead).
It is an easier way to solute your issue.
Your class have a singleton. you can add a method that is destroy this class instance. So when you call shareManager method again , it will create a new instance.
Such as:
static MyManager *sharedMyManager = nil;
+ (void)destroy
{
sharedMyManager = nil;
}
As others have stated, what you should really do is refactor your code to use dependency injection. This means that if the School class needs a MyManager instance to operate, then it should have an initWithManager:(MyManager *)manager method which should be the designated initializer. Or if the MyManager is only needed in this particular method, it should be a method parameter, e.g. createLectureWithManager:(MyManager *)manager.
Then in your tests, you could just do School *schoolToTest = [[School alloc] initWithManager:[[MyManager alloc] init]], and each test would have a new MyManager instance. You could drop the singleton pattern entirely, removing the sharedInstance method on MyManager and your application's logic would be responsible to ensure that there is only one instance that you pass around.
But sometimes, you have to work with legacy code that you can't just refactor. In these cases, you need to stub the class method. That is, you need to replace the implementation of -[MyManager sharedInstance] with an implementation that returns [[MyManager alloc] init]. This can be accomplished using the runtime to swizzle the class method, which would be the equivalent of Java reflection that you are looking for. See this for an example of how to use the runtime.
You can also do it with OCMock, which uses the runtime behind the scenes, just like mocking frameworks in Java are based on the reflection API :
MyManager *testManager = [[MyManager alloc] init];
id mock = [[OCMockObject mockForClass:[MyManager class]];
[[[mock stub] andReturn:testManager] sharedInstance];
If you don't want to refactor your code for easier unit testing then there is another solution (not perfect but works):
Create a local property of MyManager type
In setUp instantiate the property from above and swizzle the sharedInstance method with your local method (e.g. swizzle_sharedInstance)
Inside the swizzle_sharedInstance return the local property
In tearDown swizzle back to original sharedInstance and nullify the local property
I suggest a little bit different approach. You can create a mock of your sharedInstance using OCMock:
id myManagerMock = OCMClassMock([MyManager class]);
OCMStub([myManagerMock sharedManager]).andReturn(myManagerMock);
Now School implementation will use myManagerMock object, and you can stub this object to return anything you want under you test case. For example:
OCMStub([myManagerMock someMethodThatReturnsBoolean]).andReturn(YES);
It's important that after your tests, you will perform cleaning of your mock object by calling (at the end of your test method or in -tearDown):
[myManagerMock stopMocking];

Strange retain cycle warning in unit tests in Xcode

I have a service that I'm currently writing a unit test for. The code works as expected, but I'm getting a strange retain cycle warning.
[self.myService doSomethingCoolWithCompletionBlock:^(MyResponseObject *obj) {
XCTAssertNil(obj, #"obj should be nil");
}];
The XCTAssertNil(obj, #"obj should be nil"); line shows a warning in Xcode Capturing 'self' strongly in this block is likely to lead to a retain cycle.
If I change the code to the following, the warning is removed:
__weak MyService *weakService = self.myService;
[weakService doSomethingCoolWithCompletionBlock:^(MyResponseObject *obj) {
XCTAssertNil(obj, #"obj should be nil");
}];
I am using self.someService in other unit tests, and never had this issue. Anyone experienced this before?
EDIT
I have another test that has the following:
[self.myService doSomethingElseCoolWithCompletionBlock:(NSArray *results) {
XCTestAssertNotNil(results, #"results should not be nil");
}];
This doesn't give me a warning. The only difference I see is that this is checking an array, and the other is checking an object of a specific type.
assert it is macros and used self inside.
so you need create local variable with name self.
__weak id weakSelf = self;
self.fooBlock = ^{
id self = weakSelf;
XCTAssert(YES);
};
Don't do this:
#interface MyCoolTests : XCTestCase
#property (retain) id myService;
#end
#implementation MyCoolTests
-(void)testCoolness{
self.myService = [MyService new];
self.myService.callback = ^{
XCTAssert(YES);
};
// ...
}
#end
Do this:
#interface MyCoolTests : XCTestCase
#end
#implementation MyCoolTests
-(void)testCoolness{
id myService = [MyService new];
myService.callback = ^{
XCTAssert(YES);
};
// ...
}
#end
It's a limitation of XCTTestCase and it probably catches people when using the setup method.

Testing Class Methods with OCMock

Based on the documentation of OCMock, it should be possible to test class methods, but I may be misunderstanding what is and isn't possible with OCMock. Take the following example method I wish to test:
- (void)methodToTest {
[SVProgressHUD dismiss];
}
I'd like to test that dismiss is called on SVProgressHUD. I currently use the following test, but that doesn't seem to do the trick.
- (void)testMethodToTest {
// Create Mock Progress HUD
id mockProgressHUD = OCMClassMock([SVProgressHUD class]);
// Configure Mock Progress HUD
OCMStub(ClassMethod([mockProgressHUD dismiss]));
// Invoke Method to Test
[object methodToTest];
OCMVerify([mockProgressHUD dismiss]);
}
Is it possible with OCMock to test whether dismiss is called on SVProgressHUD?
Your test passes for me. Perhaps it's something subtle in your implementation? What is object? Here's my simplified version:
#interface Foo : NSObject
+ (void)dismiss;
#end
#implementation Foo
+ (void)dismiss
{
NSLog(#"Dismiss!");
}
#end
#interface Bar : NSObject
- (void)methodToTest;
#end
#implementation Bar
- (void)methodToTest
{
[Foo dismiss];
}
#end
- (void)testClassMock
{
Bar *bar = [Bar new];
id mockFoo = OCMClassMock([Foo class]);
OCMStub(ClassMethod([mockFoo dismiss]));
[bar methodToTest];
OCMVerify([mockFoo dismiss]);
}

Compilation error with OCMockito verify and NSError**

I can not compile this code:
[verify(mockedContext) deleteObject:item1];
[verify(mockedContext) deleteObject:item2];
[verify(mockedContext) save:anything()];<--compilation error for conversion id to NSError**
However I'm able to pass compilation in similar case with given macros with additional syntax:
[[given([mockedContext save:nil]) withMatcher:anything()] willReturn:nil];
Are there anything to help me pass compilation with verify?
Here is compilation error:
Implicit conversion of an Objective-C pointer to 'NSError *__autoreleasing *' is disallowed with ARC
I assume the save: method on the 'mockedContext' takes a pointer-to-pointer to NSError.
So actually, the NSError must be seen as an extra return value of the save:method. This means that you should rather setup an expectation in the first place.
I worked out a small example:
We start with the Context protocol with a simple method taking an NSError**.
#protocol Context <NSObject>
- (id)doWithError:(NSError *__autoreleasing *)err;
#end
Next is a class using this protocol, much like your SUT. I called it ContextUsingClass
#interface ContextUsingClass : NSObject
#property (nonatomic, strong) id<Context> context;
#property BOOL recordedError;
- (void)call;
#end
#implementation ContextUsingClass
- (void)call {
NSError *error;
[self.context doWithError:&error];
if (error) {
self.recordedError = YES;
}
}
#end
As you can see, when the context method doWithError: returns an error, the recordedError property is set to YES. This is something we can expect to be true or false in our test. The only problem is, how do we tell the mock to result in an error (or to succeed without error)?
The answer is fairly straight forward, and was almost part of your question: we pass an OCHamcrest matcher to the given statement, which in turn will set the error for us through a block. Bear with me, we'll get there. Let's first write the fitting matcher:
typedef void(^ErrorSettingBlock)(NSError **item);
#interface ErrorSettingBlockMatcher : HCBaseMatcher
#property (nonatomic, strong) ErrorSettingBlock errorSettingBlock;
#end
#implementation ErrorSettingBlockMatcher
- (BOOL)matches:(id)item {
if (self.errorSettingBlock) {
self.errorSettingBlock((NSError * __autoreleasing *)[item pointerValue]);
}
return YES;
}
#end
This matcher will call the errorSettingBlock if it has been set, and will always return YES as it accepts all items. The matchers sole purpose is to set the error, when the test asks as much. From OCMockito issue 22 and it's fix, we learn that pointer-to-pointers are wrapped in NSValue objects, so we should unwrap it, and cast it to our well known NSError **
Now finally, here is how the test looks:
#implementation StackOverFlowAnswersTests {
id<Context> context;
ContextUsingClass *sut;
ErrorSettingBlockMatcher *matcher;
}
- (void)setUp {
[super setUp];
context = mockProtocol(#protocol(Context));
sut = [[ContextUsingClass alloc] init];
sut.context = context;
matcher = [[ErrorSettingBlockMatcher alloc] init];
}
- (void)testContextResultsInError {
matcher.errorSettingBlock = ^(NSError **error) {
*error = [NSError errorWithDomain:#"dom" code:-100 userInfo:#{}];
};
[[given([context doWithError:nil]) withMatcher:matcher] willReturn:nil];
[sut call];
assertThatBool(sut.recordedError, is(equalToBool(YES)));
}
- (void)testContextResultsInSuccess {
[[given([context doWithError:nil]) withMatcher:matcher] willReturn:nil];
[sut call];
assertThatBool(sut.recordedError, is(equalToBool(NO)));
}
#end
Conclusion
When you call methods within your SUT which are returning errors through pointer-to-pointers, you should probably test for the different possible outcomes, rather than just verifying if the method has been called.
If your SUT is ignoring the error, then let the block you pass into the matcher keep a boolean flag to indicate that it was called like so:
- (void)testNotCaringAboutTheError {
__block BOOL called = NO;
matcher.errorSettingBlock = ^(NSError **error) {
called = YES;
};
[[given([context doWithError:nil]) withMatcher:matcher] willReturn:nil];
[sut call];
assertThatBool(called, is(equalToBool(YES)));
}
Or with simple verification:
- (void)testWithVerifyOnly {
[sut call];
[[verify(context) withMatcher:matcher] doWithError:nil];
}
PS: Ignoring errors is probably something you don't want to do...

How to start with OCMock and check if method was invoked

I'm trying to deal with OCMock. I created simple class MyClass.
#interface MyClass : NSObject
- (NSString *)simpleMethod;
#end
#implementation MyClass
- (NSString *)simpleMethod {
[self method];
return #"simple";
}
- (void)method {
NSLog(#"ABC");
}
#end
What I want to check is if method method was invoked when simpleMethod has been called. Now I've got following code but it doesn't work:
- (void)testMethodInvoked
{
id mock = [OCMockObject mockForClass:[MyClass class]];
[[mock stub] simpleMethod];
SEL selector = NSSelectorFromString(#"method");
[[mock expect] methodForSelector:selector];
[mock verify];
}
How should I test this case? I think that is pretty easy to do, but I have no idea how solve this problem.
How to create mock and call method simpleMethod which invoke method method?
Current log:
<unknown>:0: error: -[OCMockTestTests testOne] : OCMockObject[MyClass]: expected method was not invoked: methodForSelector:#selector(method)
You never actually create an object of the class that you want to test. Also, you have to expect first, then invoke the method:
- (void)testMethodInvoked
{
// first create an object that you want to test:
MyClass *object = [[MyClass alloc] init];
// create a partial mock for that object
id mock = [OCMockObject partialMockForObject:object];
// tell the mock object what you expect
[[mock expect] method];
// call the actual method on the mock object
[mock simpleMethod];
// and finally verify
[mock verify];
}
I sometimes find it useful to test "private" methods / implementations -- perhaps don't call it a unit test if that breaks some kind of orthodoxy -- but for a complex implementation I may want to verify behavior on a more granular level than through the external interface.
In any event, I will expose class extension methods by creating a category in the test class:
#interface MyClass (ExposeForTest)
- (void)method;
#end
- (void)testMyClass
{
id mock = [OCMockObject mockForClass:MyClass.class];
[[mock expect] method];
[mock simpleMethod];
}

Resources