4

I have a class which is subclass of Context. I'm unit-testing another class which have dependency to this class, and therefore I've mocked it. However, I need some methods to act as their original behavior, so I'm going to "unmock" them.

One of them is getAssets() so I wrote this and it works correctly:

Mockito.doReturn(this.getContext().getAssets()).when(keyboard).getAssets();

keyboard is the mocked instance of the class mentioned.

Since this method takes no parameters, overriding it was pretty simple.

I also need to override Context.getString(int) too. The parameter makes things difficult and it being a primitive, makes even more difficult.

I took this advice and another one, and tried writing this code:

Mockito.when(keyboard.getString(Mockito.anyInt())).thenAnswer(new Answer<String>(){
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable
         Integer arg = (Integer)invocation.getArguments()[0];
         return OuterClass.this.getContext().getString(arg.intValue());
    }
});

This compiled and executed, but gave the following exception:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> at [...] <The code above>

This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));

For more info see javadoc for Matchers class.

at android.content.Context.getString(Context.java:282)
at [...]
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:545)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551)

So the main question is How to override methods in Mockito, which have primitive parameters?

Thanks in advance

Community
  • 1
  • 1
Mousa
  • 2,190
  • 3
  • 21
  • 34
  • 1
    I can't reproduce your error. If you search for `InvalidUseOfMatchersException`, you'll find a bunch of related questions and answers. The problem is you are calling a Mockito method with one Matcher argument and one without, which is not allowed for various reasons. – Sotirios Delimanolis Sep 22 '13 at 08:18
  • Thanks for trying. Yeah, I was searching already and still doing. – Mousa Sep 22 '13 at 08:42

3 Answers3

3

You can't stub getString because it's final. Mockito can't stub final methods. Just leave it unstubbed, and you'll get its original implementation. That's what you want anyway, right?

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
  • 1
    Yes I want that, but leaving it as is, results to a `NullPointerException` inside `getString()`! I believe it's because it calls other methods which are stubbed. Thats why I'm trying to relay it from `MockedKeyboard.getString()` to `AndroidTestCase.getContext.getString()`. Isn't this the right way? – Mousa Sep 22 '13 at 10:01
  • 1
    You could try creating a mock with a default answer of `CALLS_REAL_METHODS`, then just stub those methods that you really want to stub. So `Context mockContext = mock(Context.class, CALLS_REAL_METHODS);`. You might still get into trouble if one of the methods uses a field that hasn't been initialised. – Dawood ibn Kareem Sep 22 '13 at 10:08
  • Oh, Thanks man, I was searching for such a feature :) This helps for now. – Mousa Sep 22 '13 at 10:15
1

Shouldn't it be a Answer<String> ?

Mockito.when(keyboard.getString(Mockito.anyInt())).thenAnswer(new Answer<String>(){
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
         return "foo";
    }
});

And you could rename keyboard-->mockContext, that would be more clear ?

Snicolas
  • 37,840
  • 15
  • 114
  • 173
  • Oh yeah, thanks, I was totally mixed up in my `answer()` method. I corrected it, however, the exception is the same! – Mousa Sep 22 '13 at 09:15
  • really strange error, are you sure there is no other mock invocation somewhere causing this error ? – Snicolas Sep 22 '13 at 13:10
  • This happens when you create a mocked invocation expectation with mixed parameters : both Mockito.anyXXX and a real parameter (i.e. "foo"). – Snicolas Sep 23 '13 at 05:08
  • Yes it's strange. I'm sure, I disabled all tests and just a trivial test with this setUp code remains and still the same error. Searching Internet shows that this exception despite being clear and explaining itself with a good example, shows itself in unrelated situations and the main error could be something else. – Mousa Sep 30 '13 at 07:24
0

As David Wallace noted in his answer, the Context.getString() method is final and we know that mockito cannot mock final methods. So overriding it is a bad idea too and will not work.

The "0 matchers expected, 1 recorded" info on description of InvalidUseOfMatchersException, is a vague way of saying that the method is final and we must not try to override it.

However leaving it as is won't help because the object is still a mocked object and the method won't show the expected behavior that we want from a normal object; so this option is also off the table.

At last i decided to change and improve my design. I used Interface segregation principle and divided the responsibilities of Context and Keyboard (I'm developing an IME service). In the application itself, I passed the same object of keyboard for these two, but in test I mocked the Keyboard without any bad side effects and used the TestCase.getContext() as the Context which eliminated the need to "unmock" its methods.

Community
  • 1
  • 1
Mousa
  • 2,190
  • 3
  • 21
  • 34