How to init an object with stubbed values with OCMock
Asked Answered
C

1

5

Ho do I stub a method used in the init method?

The related methods in my class:

- (id)init
{
    self = [super init];
    if (self) {
        if (self.adConfigurationType == AdConfigurationTypeDouble) {
             [self configureForDoubleConfiguration];
        }
        else {
            [self configureForSingleConfiguration];
        }
    }
    return self;
}

- (AdConfigurationType)adConfigurationType
{
    if (adConfigurationType == NSNotFound) {
        if ((random()%2)==1) {
            adConfigurationType = AdConfigurationTypeSingle;
        }
        else {
            adConfigurationType = AdConfigurationTypeDouble;
        }
    }
    return adConfigurationType;
}

My test:

- (void)testDoubleConfigurationLayout
{
    id mockController = [OCMockObject mockForClass:[AdViewController class]];
    AdConfigurationType type = AdConfigurationTypeDouble;
    [[[mockController stub] andReturnValue:OCMOCK_VALUE(type)] adConfigurationType];

    id controller = [mockController init];

    STAssertNotNil([controller smallAdRight], @"Expected a value here");
    STAssertNotNil([controller smallAdRight], @"Expected a value here");
    STAssertNil([controller largeAd], @"Expected nil here");
}

My result:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'OCMockObject[AdViewController]: unexpected method invoked: smallAdRight '

So how will I access the AdViewController in the OCMockObject?

Connivance answered 14/9, 2011 at 9:13 Comment(0)
A
13

If you use the mockForClass: method you will need to provided stubbed implementations for each and every method that are called in the mocked class. including your call into it with [controller smallAdRight] in your first test.

Instead you can use the niceMockForClass: method which will just ignore any messages which are not mocked.

Another alternative is to instantiate your AdViewController and then create a partial mock for it using the partialMockForObject: method. This way the internals of the controller class will do the main part of the work.

Just a though... are you trying to test the AdViewController or a class which uses it? It appears that you are trying to mock the entire class and then test if it still behaves normally. If you want to test that AdViewController behaves as expected when certain values are injected then your best option is most likely the partialMockForObject: method:

- (void)testDoubleConfigurationLayout {     
  AdViewController *controller = [AdViewController alloc];
  id mock = [OCMockObject partialMockForObject:controller];
  AdConfigurationType type = AdConfigurationTypeDouble;
  [[[mock stub] andReturnValue:OCMOCK_VALUE(type)] adConfigurationType];

  // You'll want to call init after the object have been stubbed
  [controller init]

  STAssertNotNil([controller smallAdRight], @"Expected a value here");
  STAssertNotNil([controller smallAdRight], @"Expected a value here");
  STAssertNil([controller largeAd], @"Expected nil here");
}
Anoa answered 14/9, 2011 at 12:17 Comment(5)
Made two small edits to Claus' answer, but essentially the proposed test implementation should pass nowDisgrace
Sometimes your init method does one-time-only configuration, and the path you want to test won't be executed with this approach. This kind of test generally also works if you just call [AdViewController alloc] in the first line.Thoughtless
Reading your post once again it's now obvious to me that you should not call init until the controller have been mocked. I hadn't noticed the first time that you actually made an implicit call to adConfigurationType through the property. I've edited my post to reflect this better.Anoa
The code above is incorrect. Calling [controller init] will call the init method on the OCPartialMockObject, not the init method on the controller object. To correctly call the controller init, use (void)[[(OCPartialMockObject *)controller realObject] init];Herl
@Herl since the mock is a partial mock, it will forward the init message to the real controller object as it wasn't stubbed or expected.Scoop

© 2022 - 2024 — McMap. All rights reserved.