Захват аргумента в Mockito

Я тестирую определенный класс. Этот класс внутренне создает экземпляр объекта "GetMethod", который передается объекту" HttpClient", который вводится в тестируемый класс.

я издеваюсь над классом "HttpClient", но мне нужно было бы изменить поведение одного метода класса" GetMethod". Я играю с ArgumentCaptor, но я, похоже, не могу получить экземпляр объекта в "когда" вызов.

пример:

HttpClient mockHttpClient = mock(HttpClient.class);
ArgumentCaptor<GetMethod> getMethod = ArgumentCaptor.forClass(GetMethod.class);
when(mockHttpClient.executeMethod(getMethod.capture())).thenReturn(HttpStatus.SC_OK);
when(getMethod.getValue().getResponseBodyAsStream()).thenReturn(new FileInputStream(source));

ответ:

org.mockito.exceptions.base.MockitoException: 
No argument value was captured!
You might have forgotten to use argument.capture() in verify()...
...or you used capture() in stubbing but stubbed method was not called.
Be aware that it is recommended to use capture() only with verify()

2 ответов


вы не можете использовать when на getMethod, потому что getMethod не является насмешкой. Это все еще реальный объект, созданный вашим классом.

ArgumentCaptor имеет совершенно другую цель. Проверка раздел 15 здесь.

вы можете сделать свой код более проверяемым. Как правило, классы, которые создают новые экземпляры других классов трудно проверить. Поместите некоторую фабрику в этот класс для создания методов get/post, затем в тестовом макете этой фабрики и сделайте ее макетом get / post методы.

public class YourClass {
  MethodFactory mf;

  public YourClass(MethodFactory mf) {
    this.mf = mf;
  }

  public void handleHttpClient(HttpClient httpClient) {
    httpClient.executeMethod(mf.createMethod());
    //your code here
  }
}

тогда в тесте вы можете сделать:

HttpClient mockHttpClient = mock(HttpClient.class);
when(mockHttpClient.executeMethod(any(GetMethod.class)).thenReturn(HttpStatus.SC_OK);

MethodFactory factory = mock(MethodFactory.class);
GetMethod get = mock(GetMethod.class);
when(factory.createMethod()).thenReturn(get);
when(get.getResponseBodyAsStream()).thenReturn(new FileInputStream(source));

обновление

вы также можете попробовать некоторые неприятные взломать, и Answer и доступ к личным частям GetMethod;) по размышлению. (Это действительно неприятный Хак)

when(mockHttpClient.executeMethod(any(GetMethod.class))).thenAnswer(new Answer() {
  Object answer(InvocationOnMock invocation) {
    GetMethod getMethod = (GetMethod) invocation.getArguments()[0];

    Field respStream = HttpMethodBase.class.getDeclaredField("responseStream");
    respStream.setAccessible(true);
    respStream.set(getMethod, new FileInputStream(source));

    return HttpStatus.SC_OK;
  }
});

хорошо, вот как я решил это. Немного запутанный, но не мог найти другого пути.

в тестовом классе:

private GetMethod getMethod;

public void testMethod() {
    when(mockHttpClient.executeMethod(any(GetMethod.class))).thenAnswer(new ExecuteMethodAnswer());
    //Execute your tested method here.
    //Acces the getMethod here, assert stuff against it.  
}

private void setResponseStream(HttpMethodBase httpMethod, InputStream inputStream) throws NoSuchFieldException, IllegalAccessException {
    Field privateResponseStream = HttpMethodBase.class.getDeclaredField("responseStream");
    privateResponseStream.setAccessible(true);
    privateResponseStream.set(httpMethod, inputStream);
}

private class ExecuteMethodAnswer implements Answer {
    public Object answer(InvocationOnMock invocation) throws FileNotFoundException,
                                                             NoSuchFieldException, IllegalAccessException {
        getMethod = (GetMethod) invocation.getArguments()[0];
        setResponseStream(getMethod, new FileInputStream(source));
        return HttpStatus.SC_OK;
    }
}