5
\$\begingroup\$

Currently working on writing out some test for an iOS enterprise application. My concern is in this set up of my overloaded constructor.

- (id)init
{
    CRMHttpClient *client = [CRMHttpClient sharedClient];
    NSOperationQueue *queue = [NSOperationQueue new];
    UIApplication *application = [UIApplication sharedApplication];
    return [self initWithCRMHttpClient:client operationQueue:queue application:application];
}

- (id) initWithCRMHttpClient:(CRMHttpClient *)client operationQueue:(NSOperationQueue *)operationQueue application:(UIApplication *)application
{
    self = [super init];
    if(self)
    {
        self.client = client;
        self.queue = operationQueue;
        self.queue.name = [[NSBundle.mainBundle bundleIdentifier] stringByAppendingString:@".CRMDataProcessingQueue"];
        self.queue.maxConcurrentOperationCount = 2;
        self.application = application;
        self.backgroundTask = UIBackgroundTaskInvalid;
    }

    return self;
}

-(id)init is called by the other classes in the project. -(id)initWithCRMHttpClient:... is called in my test class so I can inject my mocks to unit test my class. (I know I could use partial mocks for the singleton classes, but I prefer to explicitly inject the class) What bothers me about this approach is the few lines of code where I'm configuring my NSOperationQueue's name and operation count. This requires me to write this up in my -(void)setup method of my testing class.

- (void)setUp
{
    [super setUp];
    self.mockCRMHttpClient = [OCMockObject mockForClass:[CRMHttpClient class]];
    self.mockOperationQueue = [OCMockObject mockForClass:[NSOperationQueue class]];
    [[self.mockOperationQueue stub] setName:OCMOCK_ANY];
    [[self.mockOperationQueue stub] setMaxConcurrentOperationCount:2];
    self.mockApplication = [OCMockObject mockForClass:[UIApplication class]];
}

I need to stub out the calls for those set up methods so that I don't get failed calls. I my options are:

  1. Leave it as is, stubbing in the -setup
  2. Configure the operation queue in the -init method instead.
  3. Create a nice mock for mockOperationQueue
  4. Am I forgetting something?

I'm unsure of which approach to take.

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Go with #3, "Create a nice mock for mockOperationQueue." The only reason you're having to do that stubbing is that OCMock creates "strict" mocks by default. Strict mocks used to be the standard, but mock object frameworks have evolved. The problem with strict mocking is that they make tests more brittle.

For example, let's say you find the need to add another "this needs to happen when I'm setting up the operation queue" call. Suddenly, your tests will break, because the strict mock will complain, "I don't know what this new call is." You've coded things well by setting up the mock operation queue in one place, so fixing it wouldn't be hard; you'd just add another stub in your -setUp. But the test failure isn't a failure; it's noise.

OCMock was originally written when strict mocks were the norm. It was also the norm then to state expectations first, call the method of interest, then ask the mock to verify itself:

[mockView expect] addTweet:[OCMArg any];

[controller displayTweets];

[mockView verify];

But mock object frameworks have evolved since then. People figured out that having non-strict mocks was almost never a problem. And new frameworks came along that state the expectation after calling the method of interest. Using OCMockito, the example above changes to:

[controller displayTweets];

[verify(mockView) addTweet:anything()];

This restores the standard order of unit tests: "Arrange - Act - Assert" (or if you prefer, "Given - When - Then"). The verification of expectations now happens in the Assert phase, rather than sitting up in the Arrange phase. So now the test code reads more naturally, like a DSL.

The author of OCMock recognizes the importance of this evolution, and has stated:

The next major version of OCMock is planned to support nice-by-default and verification of specific calls after the fact.

\$\endgroup\$
1
  • \$\begingroup\$ To answer your now deleted comment, I was one of the first downvoters; then you changed your answer to include more helpful information. Sorry for any anxiety that may have been caused. You should join us in our chat room sometime, we need another Objective-C guru! \$\endgroup\$
    – syb0rg
    Commented Apr 12, 2014 at 0:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.