2

The code I'm working with looks something like this:-

- (MyOrange *) getAnOrangeFromBowl:(MyBowl *)bowl withSize:(NSString *)size
{
    MyOrange *orange = [bowl getAnOrangeWithSize:size];
    return orange;
}

Before you ask, no I can't just call my "MyBowl" object's method directly, it needs to be done this way for reasons unrelated to this problem.

So, I want to test that within the above method, [bowl getAnOrangeWithSize] is being called with certain parameters. I want to mock the MyBowl class as its not what's under test here, only the above method is. I also want the [bowl getAnOrangeWithSize] call to return a mock of MyOrange - in this example just for comparison purposes, but potentially I might also be doing stuff with that "orange" in the method above & I'd want to write tests on that too.

To test this I want to inject a dependency (MyBowl mock) that in turn injects another mocked dependency (MyOrange). I've run across this requirement several times and have refactored to work around it, but in this case I'm stuck.

Anyway, the test I have is:-

- (void)testThatGetAnOrangeFromBowlIsReturningAValidOrange
{
    MyOrange *mockOrange = mock([MyOrange class]);
    MyBowl *mockBowl = mock([MyBowl class]);

    [given([mockBowl getAnOrangeWithSize:@"large"]) willReturn:mockOrange];
    MyOrange *returnedOrange = [sut getAnOrangeFromBowl:mockBowl withSize:@"large"];

    assertThat(returnedOrange, is(equalTo(mockOrange)));
}

The test fails, as returnedOrange is nil. Putting a breakpoint in the code, I can see that the call to "getAnOrangeWithSize" is returning nil, so it is apparently taking no notice of the given/willReturn instructions that I specified.

I've been searching documentation & scratching my head for a while on this trying various ideas but without luck. Is there a way to get the test working, or is this something OCMockito doesn't really support (and if so, can Kiwi handle this problem)? Thanks in advance.

EDIT: It seems its possible to do this in Kiwi, as shown below:-

it(@"should return a valid Orange", ^{
    id mockOrange = [MyOrange mock];
    id mockBowl = [MyBowl mock];

    [mockBowl stub:@selector(getAnOrangeWithSize:) andReturn:mockOrange];

    MyOrange *returnedOrange = [sut getAnOrangeFromBowl:mockBowl withSize:@"whatever"];
    [[returnedOrange should] equal:mockOrange];
});

If calls to methods inside mockOrange occur in the sut's code, mockOrange could be setup as a nullMock or stubs created to handle those calls.

I am very new to Kiwi, so the above may not be optimal. I also haven't found out yet how to create a stub that only works with certain argument values passed in, so that you could create one for (using the above example) a size of @"large" returning one MyOrange instance, and @"mouldy" returning another - but I suppose that's a topic for another question if I can't find how its done.

I'd still very much like to know the OCMockito/Hamcrest equivalent of the above Kiwi code, so I'll leave this as Unanswered for now.

Andrew Sumner
  • 323
  • 3
  • 8
  • Can you show the implementation of `getAnOrangeFromBowl:`? – IluTov Oct 18 '13 at 07:20
  • Would that be helpful? I'm mocking the "MyBowl" class & specifying what a call to that method should return. My understanding is that the real MyBowl class & its methods are never used and if so, the actual implementation of MyBowl is irrelevant - unless I'm wrong? – Andrew Sumner Oct 18 '13 at 07:30
  • Maybe, I'm not too familiar with `OCMockito`. – IluTov Oct 18 '13 at 07:37

1 Answers1

2

Your example works fine in OCMockito, so your example code isn't the problem. It's code somewhere else — or, I suspect, code that isn't present:

OCMockito uses OCHamcrest matchers for given method arguments. If the argument provided isn't a matcher, it wraps it in an equalTo(…) matcher.

But if the class in question doesn't implement -isEqual:, then the equality matcher will never be satisfied. By default, OCMockito returns nil for anything that doesn't have a matching given statement.

So either the class in question needs to implement equality, or you explicitly specify a different matcher in your given statement. In such cases, I usually use sameInstance(…) to test for identity instead of equality. Sometimes the argument itself doesn't really matter, in which case I use anything().

Jon Reid
  • 20,545
  • 2
  • 64
  • 95
  • You are indeed correct - in my real code (what I posted above is obviously a mocked-up example) I hadn't specified the arguments in the **given** in exactly the same way they're called in the production code. A small mistake that my eyes glossed over dozens of times. Once that was corrected the OCMockito test now works. Thanks for taking the time to look @ the question and post your answer, I regret that all this question revealed was a mistake on my part rather than something more helpful to the community at large. Live & learn! – Andrew Sumner Oct 19 '13 at 23:40
  • Oh no, this is useful, because others will stumble in the same place. I may add a FAQ. – Jon Reid Oct 20 '13 at 00:13