Reputation: 161
I have a method that I want to test that does this:
- (void)openEmailFeedback
{
MFMailComposeViewController* controller = [[MFMailComposeViewController alloc] init];
controller.mailComposeDelegate = self;
[controller setToRecipients:@"[email protected]"];
[controller setSubject:@""];
[controller setMessageBody:@"" isHTML:NO];
[self presentViewController:controller animated:YES completion:nil];
}
And I tried testing it with
- (void)testOpenEmailFeedback
{
ViewController *vc = [[ViewController alloc] init];
// Create a partial mock of UIApplication
id mockMailComposeViewController = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mockMailComposeViewController expect] setMailComposeDelegate:vc];
[[mockMailComposeViewController expect] setToRecipients:@[@"[email protected]"]];
[[mockMailComposeViewController expect] setSubject:@""];
[[mockMailComposeViewController expect] setMessageBody:@"" isHTML:NO];
[vc openEmailFeedback];
[mockMailComposeViewController verify];
[mockMailComposeViewController stopMocking];
}
But I realized that mockMailComposeViewController is nowhere near being the same variable as the local MFMailComposeViewController *controller that's in the method. Is it possible to some how access a local variable within a method "openEmailFeedback" when testing?
Upvotes: 2
Views: 1382
Reputation: 17787
I generally use this pattern for mocking the locally created objects within the method I am testing-
MyClass *instance = [[MyClass alloc] init];
id mockMyClassObj = [OCMockObject mockForClass:[MyClass class]];
[[[mockMyClassObj stub] andReturn:instance] alloc];
And then in asserts -
// Lets say I am testing for property 'whatever' being nil after calling the method I am testing
XCTAssertNil(instance.whatever, @"whatever should be nil");
Here is the Xcode snippet for that code -
<#Class#> *<#instance#> = [[<#Class#> alloc] init];
id mock<#name#> = [OCMockObject mockForClass:[<#class#> class]];
[[[mock<#name#> stub] andReturn:<#instance#>] alloc];
Upvotes: 0
Reputation: 3016
The standard answer to this question is to use the dependency injection pattern. With OCMock and partial mocks there is another pattern that you can use, too. Simply create a factory method inside the class that has the dependency and stub that. In your ViewController:
- (MFMailComposeViewController *)createComposeViewController
{
return [[MFMailComposeViewController alloc] init];
}
- (void)openEmailFeedback
{
MFMailComposeViewController* controller = [self createComposeViewController];
controller.mailComposeDelegate = self;
// continue as normal...
In your test:
- (void)testOpenEmailFeedback
{
ViewController *vc = [[ViewController alloc] init];
// Create a partial mock of UIApplication
id mockMailComposeViewController = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mockMailComposeViewController expect] setMailComposeDelegate:vc];
[[mockMailComposeViewController expect] setToRecipients:@[@"[email protected]"]];
[[mockMailComposeViewController expect] setSubject:@""];
[[mockMailComposeViewController expect] setMessageBody:@"" isHTML:NO];
id vcPartialMock = [OCMockObject partialMockForObject:vc];
[[[vcPartialMock stub] andReturn:mockMailComposeViewController] createComposeViewController];
// continue as before...
Upvotes: 4