Search code examples
reactive-cocoaocmockito

OCMockito: RACObserve a mocked object always fail


code like this:

@implementation MyClass

- (void)func
{
//MyOtherClassObject is an object passed in when initialising MyClass
    NSArray *signals = @[[RACObserve(MyOtherClassObject, prop) subscribeNext:^{{}]];
}

@end

@implementation MyTest

- (void)testSomething
{
    MyOtherClass *mock = mock([MyOtherClass class]);
    MyClass *myObject = [[MyClass alloc] initWithOtherObject:mock]
    [myObject func];  //this won't work since RACObserve will return nil instead of a valid signal
}

@end

So, is there any way I can get a mocked object RACObserved like normal object?


Solution

  • This is because OCMockito (and OCMock as well) breaks Key-Value Observing, that is, you won't get any KVO "callbacks" from the mocked objects. And RACObserve uses KVO under the hood. There are many possible workarounds:

    1. As described in OCMockito docs:

    How do you stub a property so that KVO works? Use stubProperty(instance, property, value). For example:

    stubProperty(mockEmployee, firstName, @"fake-firstname");
    

    I haven't used OCMockito so I can't tell whether this will really work with RAC.

    1. Use a real object instead of a mock. It will work for some cases (when the object is really simple and you just need to change a property). Of course it shouldn't be used when it will break isolation of the unit test (for example, by sending a network request or accessing a database / file system).

    2. Extract the signal returned by RACObserve to a property and stub. Instead of

    NSArray *signals = @[[RACObserve(MyOtherClassObject, prop) subscribeNext:^{{}]];

    do:

    NSArray *signals = MyOtherClassObject.prop

    where MyOtherClassObject.prop will return RACObserve(self, prop). Then you can easily stub MyOtherClassObject.prop and return any signal, for example [RACSignal return:] for getting a single value synchronously.