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@email.com"]];
[[[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.

Tomasz Szulc
  • 4,217
  • 4
  • 43
  • 79
Abdalrahman Shatou
  • 4,550
  • 6
  • 50
  • 79
  • 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. – Erik Doernenburg Feb 24 '14 at 13:45

2 Answers2

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.

Ben Flynn
  • 18,524
  • 20
  • 97
  • 142
-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@email.com"]];
[[[partialComposerMock expect] andReturn:nil] setSubject:nil];
[[[partialComposerMock expect] andReturn:self.frontViewController] presentingViewController];
...
[partialComposerMock verify];
Tomasz Szulc
  • 4,217
  • 4
  • 43
  • 79
  • 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: http://stackoverflow.com/a/20055365/1149647. What is the difference in my code? – Abdalrahman Shatou Feb 23 '14 at 14:01
  • 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@email.com"] – Abdalrahman Shatou Feb 23 '14 at 15:15
  • is `Text_Email_Me_Email` equals to `email@email.com`? 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. – Tomasz Szulc Feb 23 '14 at 17:59