36

At my day job I've been spoiled with Mockito's never() verification, which can confirm that a mock method is never called.

Is there some way to accomplish the same thing using Objective-C and OCMock? I've been using the code below, which works but it feels like a hack. I'm hoping there's a better way...

- (void)testSomeMethodIsNeverCalled {
    id mock = [OCMockObject mockForClass:[MyObject class]];
    [[[mock stub] andCall:@selector(fail) onObject:self] forbiddenMethod];

    // more test things here, which hopefully
    // never call [mock forbiddenMethod]...
}

- (void)fail {
    STFail(@"This method is forbidden!");
}
Justin Voss
  • 6,294
  • 6
  • 35
  • 39

6 Answers6

64

Since r69 of OCMock, it's possible to reject a method call http://svn.mulle-kybernetik.com/OCMock/trunk/Source/Changes.txt

Nice mocks / failing fast When a method is called on a mock object that has not been set up with either expect or stub the mock object will raise an exception. This fail-fast mode can be turned off by creating a "nice" mock:

id mock = [OCMockObject niceMockForClass:[SomeClass class]]

While nice mocks will simply ignore all unexpected methods it is possible to disallow specific methods:

[[mock reject] someMethod]

Note that in fail-fast mode, if the exception is ignored, it will be rethrown when verify is called. This makes it possible to ensure that unwanted invocations from notifications etc. can be detected.

Quoted from: http://www.mulle-kybernetik.com/software/OCMock/#features

InsertWittyName
  • 3,930
  • 1
  • 22
  • 27
19

As far as I know OCMock will fail automatically when you call verify and methods that have not been recorded were called. A mock that wouldn't complain if unexpected methods were called is called a "nice mock".

- (void)testSomeMethodIsNeverCalled {
    id mock = [OCMockObject mockForClass:[MyObject class]];

    [mock forbiddenMethod];
    [mock verify]; //should fail
}
Johannes Rudolph
  • 35,298
  • 14
  • 114
  • 172
  • That totally worked! I didn't expect it to be so simple. Just to play devil's advocate, do you think this hides the intent of the test? – Justin Voss May 03 '10 at 17:52
  • @Justin: Well, it requires the reader to know about OCMock's behavior in this case, which is not all too obvious. Putting a comment right next to the mock verify call should suffice to make it clear what should happen. Like: `// verify should fail because we called an unexpected method on the mock.` – Johannes Rudolph May 04 '10 at 06:53
10

Starting from version 3.3 OCMock has OCMReject macro.

    id mock = OCMClassMock([MyObject class]);
    OCMReject([mock forbiddenMethod]);

    // exception will raise
    [mock forbiddenMethod];
Ruslan Mansurov
  • 1,281
  • 16
  • 23
3

You may also find it necessary to ensure a method is never being called on an object you are partially mocking.

I created a macro for this:

#define andDoFail andDo:^(NSInvocation *invocation) { STFail(@"Should not have called this method!"); }

I use it like this:

[[[_myPartialMock stub] andDoFail] unexpectedMethod];
Ben Flynn
  • 18,524
  • 20
  • 97
  • 142
0

To make sure your method not called, I think we have to do an assert before the testMethod called. So make sure put OCMReject before you run the test method to listen which method will trigger when runs testMethod:

OCMReject([mock someMethod]);
[mock testMethod];
Tai Le
  • 8,530
  • 5
  • 41
  • 34
  • 2
    While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please [include an explanation for your code](//meta.stackexchange.com/q/114762/269535), as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. **Flaggers / reviewers:** [For code-only answers such as this one, downvote, don't delete!](//meta.stackoverflow.com/a/260413/2747593) – Patrick Aug 29 '17 at 19:27
0

You could also try something like this, a la JavaScript:

- (void)aMethod {
   __block BOOL b = NO;

   id mock = [OCMockObject mockForClass:[UIView class]];
   [[[mock stub] andDo:^(NSInvocation *i) { b = YES; }] resignFirstResponder];
   [mock resignFirstResponder];

   NSLog(@"And b is: %i", b); // This reads "And b is: 1" on the console
}

I'm not sure if there are any leaks associated with this code. I got the idea from reading this page: http://thirdcog.eu/pwcblocks/