Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stub a key-value lookup in an implementation-agnostic way #68

Open
iamleeg opened this issue Feb 13, 2014 · 8 comments
Open

Stub a key-value lookup in an implementation-agnostic way #68

iamleeg opened this issue Feb 13, 2014 · 8 comments

Comments

@iamleeg
Copy link

iamleeg commented Feb 13, 2014

Imagine trying to sort some objects:

id object1 = [OCMockObject mockForProtocol:@protocol(MyModel)];
id object2 = [OCMockObject mockForProtocol:@protocol(MyModel)];
[[[object1 stub] andReturn:@"B"] name];
[[[object2 stub] andReturn:@"A"] name];
//...
XCTAssertEqualObjects(sortedArray, @[object2, object1]);

I don't want to depend on how the sort is implemented, just on whether it works. If I use sortedArrayUsingDescriptors: then the test will fail because the implementation calls valueForKey: rather than calling the accessor directly. What I'd like to do is something like this:

[[[object1 stub] value:@"B"] forKey:@"name"];
[[[object2 stub] value:@"A"] forKey:@"name"];

which is both a convenience for stubbing -name, and stubs -valueForKey:.

@carllindberg
Copy link
Contributor

Normally, -valueForKey: calls your accessor method. What kind of object is it, such that it has a special implementation of valueForKey: which avoids calling the method? I don't think OCMockObject does anything special there... but since you are a mock for a protocol, and -valueForKey: is probably not a method in the protocol, the mock object may not have any idea of what to do.

@iamleeg
Copy link
Author

iamleeg commented Feb 18, 2014

@carllindberg "since you are a mock for a protocol, and -valueForKey: is probably not a method in the protocol, the mock object may not have any idea of what to do" is exactly the issue. I -stub my key's accessor, but then -[NSProxy valueForKey:] gets called in the test. I don't particularly want to have to stub that too, I want a single action that says "whatever route the SUT uses to grab the value for this key, give it this value".

@carllindberg
Copy link
Contributor

From one perspective... when you have an object which conforms to a protocol, normally that means you should be restricting calls to those methods only. The fact you are calling methods outside the protocol (even indirectly via sortedArrayUsingDescriptors:) can be considered an error in itself. So, the behavior does make sense. When you are using a mock object for a protocol or a class, you normally have to provide stub implementations for every method you use... that would include methods you indirectly use. Obviously, since most objects inherit from NSObject, you can get away with calling those methods at runtime (even if they are not even part of the NSObject protocol).

Is it possible for you to use a partial mock object? That should use the normal implementation while being able to stub out only certain methods.

It might be possible though to have a special method on OCMockObject to add all the NSKeyValueCoding implementations to the mock object, as a special case to aid with this sort of thing, so that the mock objects have the same implementation as NSObject. That will not help if the target class has special overridden versions of valueForKey: (such as NSManagedObject) but it might work out. That would probably make it impossible to stub or expect those methods though.

@erikdoe
Copy link
Owner

erikdoe commented Feb 19, 2014

I see your point. In the case you describe it's clearly inconvenient to have to write these two lines instead of just one:

[[[object1 stub] andReturn:@"B"] name];
[[[object1 stub] andReturn:@"B"] valueForKey:@"name"];

Unfortunately, it's not straight-forward from a mock object implementation perspective. Do you implement/stub all methods, including, for example, valueForKeyPath: and dictionaryWithValuesForKeys:? Do you merge all the stubs?

Then there is the problem that Carl mentioned, anything that OCMock could do would be the standard (naive) KV implementation. What is the expectation if the class that gets mocked overrides some of the KV methods?

To be honest, I'm not a big fan of partial mocks but this seems like a good situation to use them.

@erikdoe
Copy link
Owner

erikdoe commented Feb 19, 2014

Thinking about it more, I could see a middle ground that would still require an extra call, though:

[[[[object1 stub] includingKeyValueCoding] andReturn:@"B"] name];

This is something to consider for a later version of OCMock.

@LowAmmo
Copy link

LowAmmo commented Feb 19, 2014

@erikdoe - That's a pretty good compromise... And easily allows for an opt-in so that only consumers that want it need to account for the side effects...

+1

@iamleeg
Copy link
Author

iamleeg commented Feb 19, 2014

👍

@ibsh
Copy link

ibsh commented Jan 5, 2015

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants