Mockito: InvalidUseOfMatchersException

92 צפיות
מעבר להודעה הראשונה שלא נקראה

David Laci

לא נקראה,
28 באוק׳ 2015, 5:46:4128.10.2015
עד mockito
Hi,

I have a problem in defining some Mockito rules

import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Test;

public class MockitoTest {

  public class Config {
    public String apiEndpoint() {
      return "http://host:port/api";
    }
  }

  public class RestHelper {
    public String post(final String myArbitraryParameter, final String url) {
      return "post on URL: " + url;
    }
  }

  @Test
  public void strangeMockitoTest() {
    final Config config = mock(Config.class);
    when(config.apiEndpoint()).thenReturn("mockApiEndpoint");
    final RestHelper restHelper = mock(RestHelper.class);
    when(restHelper.post(eq("asd"), eq(config.apiEndpoint() + "Laci"))).thenReturn("response");
    restHelper.post("first", "http://mockedHost:port/api");
  }

}


here in line:

 when(restHelper.post(eq("asd"), contains(config.apiEndpoint() + "Laci"))).thenReturn("response");

i am recieving a 
org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> at com.bmw.sf.mybmw.business.facade.facadeformat.MockitoTest.strangeMockitoTest(MockitoTest.java:29)

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"));

As you can see I-m not using raw values.

I observed, that this behaviour only appears, if a mocked function is called in a matchers expression,
and only if the inline mocked function is the second parameter of the outtermost mocked function.

e.g:

 public class RestHelper {
    public String post(final String myArbitraryParameter, final String url) {
      return "post on URL: " + url;
    }
  }

 when(restHelper.post(eq("asd"), eq(config.apiEndpoint() + "Laci"))).thenReturn("response");
-> FAILS

 public class RestHelper {
    public String post(final String myArbitraryParameter, final String url) {
      return "post on URL: " + url;
    }
  }

 when(restHelper.post(eq("asd"), eq("http://mockedHost:port/api"))).thenReturn("response");
-> OK


     public class RestHelper {
        public String post(final String url) {
          return "post on URL: " + url;
        }
      }

     when(restHelper.post(eq("http://mockedHost:port/api"))).thenReturn("response");
    -> OK

    Does somebody has some ideea, what is happening, is this a BUG, or this is caused by some underlying architecture detisions?
    MockitoTest.java

    Eric Lefevre-Ardant

    לא נקראה,
    28 באוק׳ 2015, 9:53:3528.10.2015
    עד moc...@googlegroups.com
    Yes, this is due to an architecture that underlies pretty much all of Mockito.

    The big idea in Mockito, as I gather, is the ability to express behaviours in ways that feel reasonably natural:

      when(myMock.doSomething(value)).thenReturn(result)

    Actually, when executed, the byte-code is equivalent to the following:
      Object tempResult = myMock.doSomething(value);
      when(tempResult).thenReturn(result)

    The magic is from the fact that the myMock object managed by Mockito. Mockito intercepts the call to doSomething() and change its internal state to expect further configuration (in that case, it becomes aware of the when() clause and then knows that it will next receive the result value).

    Now, consider your code (removing the argument matchers):
        when(restHelper.post("asd", config.apiEndpoint())).thenReturn("response");

    as per the example above, this is actually equivalent to:
        String endPoint = config.apiEndpoint();
        String tempResult = restHelper.post("asd", endPoint);
        when(restHelper.post("asd", tempResult)).thenReturn("response");

    On the first line, Mockito intercepts the call to config.apiEndpoint() and change its internal state to expect further configuration. 
    On the second line, Mockito intercepts the call to restHelper.post, and... fails, as it was expecting something like a when(). Instead, you instructed Mockito to configure 2 mocks at the same time.

    Now, I realise that the error message you got mentions argument matchers. That's OK. It only means that Mockito failed on a slightly more unusual combinaison of arg matchers and mock configuration. It's a bit hard to describe that, but the idea is exactly the same.

    Personally, I like using only hard-coded values when configuring mocks anyway. I actually have no problem copy/pasting the same primitive values as many times as necessary, because I find them to make tests more obvious to read.

    --
    You received this message because you are subscribed to the Google Groups "mockito" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to mockito+u...@googlegroups.com.
    To post to this group, send email to moc...@googlegroups.com.
    Visit this group at http://groups.google.com/group/mockito.
    To view this discussion on the web visit https://groups.google.com/d/msgid/mockito/bea20ef4-e0c5-46b5-ab0e-e4f74cf88e77%40googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.

    השב לכולם
    השב למחבר
    העבר לנמענים
    0 הודעות חדשות