OCMock - Unexpected Method Invoked although expected
Asked Answered
C

2

3

Here is the tested code:

if ([MFMailComposeViewController canSendMail]) {
        MFMailComposeViewController *mailComposeController = [[MFMailComposeViewController alloc] init];
        [mailComposeController setSubject:nil];
        [mailComposeController setToRecipients:@[Text_Email_Me_Email]];
        [mailComposeController setMailComposeDelegate:self];
        [self.frontViewController presentViewController:mailComposeController
                                               animated:YES
                                             completion:nil];

    }

Here is the testing code:

 id mailComposerMock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[[mailComposerMock stub] andReturnValue:@YES] canSendMail];
[[[mailComposerMock stub] andReturn:mailComposerMock] alloc];
(void)[[[mailComposerMock stub] andReturn:mailComposerMock] init];
[[[mailComposerMock expect] andReturn:nil] setMailComposeDelegate:self.contactItemManager];
[[[mailComposerMock expect] andReturn:nil] setToRecipients:@[@"[email protected]"]];
[[[mailComposerMock expect] andReturn:nil] setSubject:nil];
[[[mailComposerMock expect] andReturn:self.frontViewController] presentingViewController];
[self.contactItemManager handleSelectionOfContentItemWithTitle:Text_Contact_Me_Email_Me];
[mailComposerMock verify];

The error states:

[theTestingClass testEmailMe] failed: OCMockObject[MFMailComposeViewController]: unexpected method invoked: setSubject:nil 

And as you can see, I am calling setSubject already.

Collencollenchyma answered 23/2, 2014 at 13:1 Comment(1)
Is it possible that setSubject: is called more than once? Does the problem go away when you stub, rather than expect, the method? Also, from looking at the code you posted it's not clear why you are expecting the presentingViewController method on the mail composer mock.Peregrination
P
1

I haven't determined exactly why this is happening, but you can get around it if you build in some dependency injection:

- (void)handleSelectionOfContentItemWithTitle:(NSString *)title mailComposeViewController:(MFMailComposeViewController *)mailComposeViewController;

Additionally, avoid returning nil in void methods.

[[mailComposerMock expect] setSubject:nil];

If you were to pass your mock object into a method that could take the compose controller as an argument, your test should work.

Alternatively you could mock a custom factory method:

+ (MFMailComposeViewController*)mailComposeViewController
{
    return [[MFMailComposeViewController alloc] init];
}

Not quite an answer, but hopefully a helpful workaround. I avoid mocking alloc and init personally.

Potshot answered 24/2, 2014 at 21:32 Comment(0)
S
-1

I think that here:

(void)[[[mailComposerMock stub] andReturn:mailComposerMock] init];

should be passed mock from method partialMockForObject not mockForClass.

My proposition is that you should create second mock which is made by partialMockForObject

Finally:

id mailComposerMock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
MFMailComposeViewController *mailComposer = [MFMailComposeViewController new];
id partialComposerMock = [OCMockObject partialMockForObject:mailComposer];
...
(void)[[[mailComposerMock stub] andReturn: partialComposerMock] init];

[[[partialComposerMock expect] andReturn:nil] setMailComposeDelegate:self.contactItemManager];
[[[partialComposerMock expect] andReturn:nil] setToRecipients:@[@"[email protected]"]];
[[[partialComposerMock expect] andReturn:nil] setSubject:nil];
[[[partialComposerMock expect] andReturn:self.frontViewController] presentingViewController];
...
[partialComposerMock verify];
Stich answered 23/2, 2014 at 13:18 Comment(3)
I don't understand what this line does" (void)[[[mailComposerMock stub] andReturn: partialComposerMock] init]; I am following the same answer here but for alert view: https://mcmap.net/q/1671545/-using-ocunit-to-test-if-an-uialertview-is-presented. What is the difference in my code?Collencollenchyma
BTW, this answer will cause the other hand error: file:///%3Cunknown%3E: test failure: -[ContactMeItemSelectionManagerTests testEmailMe] failed: OCPartialMockObject[MFMailComposeViewController]: expected method was not invoked: setToRecipients:@[@"[email protected]"]Collencollenchyma
is Text_Email_Me_Email equals to [email protected]? There should be the same value. if this is a const you should cast it manifestly to NSString. If it's not equal you can pass as parameter [OCMArg any] in your test to simulate that some argument other than nil will be passed when calling this method.Stich

© 2022 - 2024 — McMap. All rights reserved.