OCMock Unrecognized Selector for mocked object

1.8k Views Asked by At

I have an error that seems to be related to OCMockObject PartialMock. When mocking an object and stubbing a method I get this unrecognized selector error which I'm pretty sure is an order or casting issue. Here's my test

STV_StreamServer *server = [NSEntityDescription insertNewObjectForEntityForName:@"STV_StreamServer"inManagedObjectContext:context];
id mockServer = [OCMockObject partialMockForObject:server];
[[[mockServer stub] andReturnValue:@YES] localURLPresent];
[[[mockServer stub] andReturnValue:@NO] remoteURLPresent];

id mockSUT = [OCMockObject partialMockForObject:sut];


[[[mockSUT stub] andReturnValue:@YES] canLiveStream:nil];

sut.streamServer = mockServer;

NSError *err = [mockSUT checkStreamingPlayabilityForUser:[self getUser:NO]];

XCTAssertNil(err, @"An error occured when basic user tried local playback");

sut is a STV_MediaServer. The error I get is [STV_MediaServer-0xb39aba0-407898154.181220 setStreamServer:]: unrecognized selector sent to instance 0xb39aba0. So first off I see that the object type seems wrong since it now includes what looks like a memory location. This occurs when I mock my sut. I'm sure it's an order issue. Been googling for hours.

2

There are 2 best solutions below

3
On

I have not reproduce your issue, but I think it is not possible to stub properties(the CoreData ones declared with @dynamic) in managed objects.

For this case you show, you can simply set the properties to the values you want - no need for stubs here.

4
On

You're getting this error because you're invoking the method on mockSUT. Due to the way CoreData generates property accessors OCMock can't copy their implementations to the partial mock and therefore they can't be found by the ObjC runtime.

When checkStreamingPlayabilityForUser eventually calls self.streamServer self is actually mockSUT and the method 'streamServer' cannot be found.

This would work fine if you simply configured the managed object the way you need it for this specific test.

FWIW you should never try to mock instances of NSManagedObject, the preferred way to perform these types of test is to simply create test objects in the unit test that fit the configuration you need.


Data driven tests with Core Data:

It will help if you create a subclass of SenTestCase or XCTestCase that can manage the CoreData bits for you. This test case subclass should make available an instance of NSManagedObjectContext for your test to use.

An actual test might look something like this:

@implementation PeopleViewControllerTest
  - (void)testSomething {
    NSMutableArray *people = [NSMutableArray new];
    [self.managedObjectContext performBlockAndWait:^{
      for (int i=0; i < 100; i++) {
        Person *p = [NSEntityDescription insertNewObjectForEntityForName:@"Person"
                                                  inManagedObjectContext:self.managedObjectContext];
        p.firstName = [NSString stringWithFormat:@"First%d", i];
        [people addObject:p];
      }
    }];
    //return the people
    PeopleViewController *pvc = ...;
    id mockPVC = [OCMockObject partialMockForObject:pvc];
    [[[mockPVC stub] andReturn:people] fetchedPeople];

    //make sure the view controller behaves properly with these 100 people
  }
@end

So instead of creating 100 mock instances of NSManagedObject we just create 100 actual objects.

We're not trying to test CoreData, just our logic thats built on top of some set of NSManagedObjects. Therefore creating concrete instances of NSManagedObject is fine, but they should be configured to exercise your application's logic.

i.e. if you wanted to check email address validation, you might have:

p.emailAddress = @"notvalid";

//later with some mock object [[[partialMock expect] andReturn:p] person];