stub [[SomeClazz alloc] init] not work but accepte

2020-07-22 10:19发布

问题:

My function under test is very simple:

@implementation MyHandler
...
-(void) processData {
  DataService *service = [[DataService alloc] init];
  NSDictionary *data = [service getData];
  [self handleData:data];
}

@end

I use OCMock 3 to unit test it.

I need to stub the [[DataService alloc] init] to return a mocked instance, I tried the answer from this question (which is an accepted answer) to stub [[SomeClazz alloc] init]:

// Stub 'alloc init' to return mocked DataService instance,
// exactly the same way as the accepted answer told
id DataServiceMock = OCMClassMock([DataService class]);
OCMStub([DataServiceMock alloc]).andReturn(DataServiceMock);
OCMStub([DataServiceMock init]).andReturn(DataServiceMock);

// run function under test
[MyHandlerPartialMock processData];

// verify [service getData] is invoked
OCMVerify([dataServiceMock getData]);

I have set break point in function under test, I am sure [service getData] is called when run unit test, but my above test code (OCMVerify) fails. Why?

Is it because the function under test is not using my mocked DataService? But the answer accepted in that question tells it should work. I get confused now...

I want to know how to stub [[SomeClazz alloc] init] to return mocked instance with OCMock?

回答1:

You cannot mock init as it is implemented by the mock object itself. The reason that mocking init works in the answer you linked is because it is a custom init method. If you do not want to use dependency injection, you will have to write a custom init method for DataService that you can mock.

In your implementation add a custom init method:

// DataService.m
...
- (id) initForTest
{
    self = [super init];
    if (self) {
        // custom initialization here if necessary, otherwise leave blank
    }

    return self;
}
...

Then update MyHandler implementation to call this initForTest:

@implementation MyHandler
...
-(void) processData {
  DataService *service = [[DataService alloc] initForTest];
  NSDictionary *data = [service getData];
  [self handleData:data];
}

@end

And finally update your test to stub initForTest:

id DataServiceMock = OCMClassMock([DataService class]);
OCMStub([DataServiceMock alloc]).andReturn(DataServiceMock);
OCMStub([DataServiceMock initForTest]).andReturn(DataServiceMock);

// run function under test
[MyHandlerPartialMock processData];

// verify [service getData] is invoked
OCMVerify([dataServiceMock getData]);

Feel free to rename initForTest, so long as it isn't called init.