OCMOCK_ANY for values

Discussion of the OCMock framework. If you have patches we would prefer you to send them to the mailing list, but attaching them to a topic is possible, too.

Re: OCMOCK_ANY for values

Postby chrispix » 16 Feb 2012, 00:33

This continues to plague me. What about allowing for selector-based matching? Something like:

Code: Select all
[mockController expect:@selector(someMethodWithInt:otherIntArgument:)];
[mockController reject:@selector(someMethodWithInt:otherIntArgument:)];
[[mockController stub:@selector(someMethodWithInt:otherIntArgument:)] andReturn:@"foo"];


Using either of these would ignore all arguments and match any invocation. It wouldn't help when it's important to match one or more arguments specifically, but I suspect it would work for 90% of cases.
chrispix
 
Posts: 7
Joined: 07 May 2010, 19:32

Re: OCMOCK_ANY for values

Postby shem » 16 Aug 2012, 10:03

Here are a couple of new ideas here, because this is a big hole in the testing framework in my opinion:

(1) Extending OCMockRecorder to have a more generalized method of ignoring matching any argument besides the current mechanism that only works with objects (and has an interesting implementation for pointers...)

- (id)ignoresArgAtIndex:(NSUInteger)index; //(or maybe just restrict it to non-object 'values' only, which may look like 'ignoresValueArgAtIndex')

And then in the 'matchesInvocation' method, you could just check if that particular index had been registered as needing to be ignored, and if so, ignoring that particular argument. Seems pretty simple, and technically still fits the recorder mantra.

(2) Another way of achieving this could be by just providing a 'alwaysMatchesInvocation:^(NSInvocation invocation /* or potentially just providing the array of parameters */) {...}' (assuming the arg types and arg count is the same as the method signature) bit on the recorder, and then leaving the assertion of what's allowed and disallowed explicitly to the definition in the block. The default behavior could be to fallback to the original matching behavior if the matcher fails. It's a bit heavy handed, but could work -- very similar to the 'doBlock' variant, except this one would be evaluated to determine if the invocation should match.
shem
 

Re: OCMOCK_ANY for values

Postby shem » 16 Aug 2012, 11:07

Even better solution now that I've given it more thought:

Implement a stack of parameter matcher objects (ie. OCMConstraint). This could be a new implementation of OCMArg's various parameter matchers.

Add various methods to OCMArg:
- (int)anyInt;
- (float)anyFloat;
etc.

And, when these are called, you add the generic 'any' matcher to the stack (representing the array of parameters for the method), and just return a dummy value of the primitive type. Then, once the recorded expectation gets set (when the method attempts to be forwarded), you just pop everything off the stack, and record those inside the OCMockRecorder and evaluate them all later. Then, you can effectively match any argument - and it creates a good abstraction. I may help flush out an implementation here if you'd like. This could also be used for nice ways of capturing input as well (ala the 'Capture' interface of EasyMock).

I like it.
shem
 

Re: OCMOCK_ANY for values

Postby erik » 28 Sep 2012, 00:01

This sounds interesting. I am not sure how this would behave on multiple invocations of the stubbed method. Would you keep a copy of the stack? Maybe some code would help. ;)
erik
 
Posts: 90
Joined: 10 Oct 2009, 15:22
Location: Hamburg, Germany

Re: OCMOCK_ANY for values

Postby Guest » 27 Jan 2013, 16:31

Hi,
Any chance of a quick change implementing this specifically for selector arguments (SEL)?
+[OCMArg anyPointer] does match selectors (preventing compiler warnings) but +[OCMArg resolveSpecialValues] doesn't identify it, since the SEL type is indicated by ':' rather than '^' in the method signature. This seems easy to change, and is useful for many APIs which take targets and selectors.
Thanks
Guest
 

Re: OCMOCK_ANY for values

Postby Guest » 30 Jan 2013, 10:19

... Sorry, that might have been less than polite. I'll try to make this change myself, including unit tests and other specific pointer types (such as const char *, which is also not encoded as ^something ). I hope to find time for it in the next few days.
BTW, another small question: the constant value indicating anyPointer is just a fixed address, which I suppose wouldn't be used for objects but I'm not sure about general pointers... Maybe it would be better to define a static variable in OCMock, and return a pointer to it?
Guest
 

Re: OCMOCK_ANY for values

Postby erik » 02 Jul 2013, 13:47

It's probably an understatement to say that this has been going on for a while now. Obviously there is no silver bullet but I believe that more than 80% of the cases would be covered by a simple solution along the lines suggested here:

chrispix wrote:(1) Extending OCMockRecorder to have a more generalized method of ignoring matching any argument besides the current mechanism that only works with objects (and has an interesting implementation for pointers...)

- (id)ignoresArgAtIndex:(NSUInteger)index; //(or maybe just restrict it to non-object 'values' only, which may look like 'ignoresValueArgAtIndex')

And then in the 'matchesInvocation' method, you could just check if that particular index had been registered as needing to be ignored, and if so, ignoring that particular argument. Seems pretty simple, and technically still fits the recorder mantra.


In the end I added an approach based on this idea, but even simpler. Rather than "turning off" individual arguments by their index, you can now tell OCMock to completely ignore all non-object args in a given invocation, like:
Code: Select all
[[[mock expect] ignoringNonObjectArgs] someMethodWithIntArgument:0]

I am aware that it could be the case that one argument must be matched exactly while another argument doesn't matter and both of them are primitives/structs. In that case the suggestion above would be needed. If there's demand I may add that in addition at a later point.
erik
 
Posts: 90
Joined: 10 Oct 2009, 15:22
Location: Hamburg, Germany

Re: OCMOCK_ANY for values

Postby erik » 02 Jul 2013, 13:49

Guest wrote:Any chance of a quick change implementing this specifically for selector arguments (SEL)?
+[OCMArg anyPointer] does match selectors (preventing compiler warnings) but +[OCMArg resolveSpecialValues] doesn't identify it, since the SEL type is indicated by ':' rather than '^' in the method signature. This seems easy to change, and is useful for many APIs which take targets and selectors.


Thanks for the suggestion. I've added an anySelector method to OCMArg now.
erik
 
Posts: 90
Joined: 10 Oct 2009, 15:22
Location: Hamburg, Germany

Re: OCMOCK_ANY for values

Postby William » 23 Mar 2016, 16:52

My lib (a theme managing lib) heavily uses method-forwarding tech as OCMock does.
When I want to pass an object into where it supposed to be a primitive argument (or C struct), I do something as following:
I
Code: Select all
[(UIView *)view.knw_themable.argAtIndex(0, @42) setTag:NSNotFound];


As you can tell,
Code: Select all
@42
is the real argument, and what passed into the method is not important at all.
And because
Code: Select all
.knw_themable
returns a
Code: Select all
NSProxy
which actually receive the method and all the arguments (real arguments and those fake), all the replacement can be done inside it.

I believe OCMock can do something similar like:
Code: Select all
[mock.stub.argAtIndex(0, OCMOCK_ANY) doWithInt:NSNotFound];


More about my lib:
https://github.com/coppercash/KnightWatson
William
 

Previous

Return to OCMock



cron