ocmock icon indicating copy to clipboard operation
ocmock copied to clipboard

OCMock 3: Infinite recursion when mocking object that does selector forwarding

Open patrickhartling opened this issue 10 years ago • 6 comments

The code base I work on includes several classes that utilize selector forwarding. Each forwarding class has the following code:

- (BOOL)respondsToSelector:(SEL)aSelector
{
    return [super respondsToSelector:aSelector] || [self.otherObject respondsToSelector:aSelector];
}

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if ([self.otherObject respondsToSelector:aSelector])
    {
        return self.otherObject;
    }
    else
    {
        return [super forwardingTargetForSelector:aSelector];
    }
}

When migrating to OCMock 3 from version 2, I found that infinite recursion would occur if I simply created a partial mock for an instance of one of these classes. I worked around it by changing the first line of ‑forwardingTargetForSelector: to be the following:

    if ([_otherObject respondsToSelector:aSelector])

I made a simple test class that demonstrates the problem.

patrickhartling avatar Jul 29 '14 20:07 patrickhartling

With the recent changes made in response to some of the other issues reported this problem seems to have been fixed, too. Could you retry whether it works for you now?

erikdoe avatar Aug 20 '14 14:08 erikdoe

I will give it a try and get back to you as soon as I can. Thanks for the update.

patrickhartling avatar Aug 20 '14 15:08 patrickhartling

The problem still occurs with the referenced example test case.

patrickhartling avatar Aug 28 '14 22:08 patrickhartling

Any update here? I've just recently upgraded some projects to OCMock 3 and am seeing the same issue partial mocking objects with classes that use selector forwarding.

cutz avatar Sep 16 '15 20:09 cutz

Many years later I looked into this again. There's still no fix. The problem is that self.otherObject results in sending the method otherObject, which ends up in the partial mock's forwardingTargetForSelector, which then invokes forwardingTargetForSelector on the object, which does self.otherObject... Somehow, when OCMock doesn't do its magic, some other place must detect this condition and break the recursion. I don't know how to do this in OCMock.

erikdoe avatar May 11 '21 20:05 erikdoe

That sounds like a challenging problem indeed. For what it's worth, we haven't seen this crop up in many years. We needed to reduce reliance on selector forwarding, and avoiding the infinite recursion in tests was an excellent motivator.

patrickhartling avatar May 12 '21 15:05 patrickhartling