Использование класса ArgumentCaptor в Mockito для соответствия дочернему классу - PullRequest
8 голосов
/ 23 марта 2011

Код ниже показывает мою проблему.По сути, я пытаюсь использовать ArgumentCaptor от Mockito для проверки того, что метод был вызван один раз с определенным конкретным классом.Я хотел бы использовать ArgumentCaptor здесь, если это возможно, но я начинаю подозревать, что вместо этого мне нужно использовать пользовательский ArgumentMatcher.

Проблема в том, что строка Mockito.verify(mocked).receive(captor.capture()); (Редактировать: это добавлено в код ниже)завершается с исключением TooManyActualInvocations (2 вместо 1).Я хотел бы понять, почему это происходит - это плохая реализация Mockito или ограничение, вызванное стиранием типов дженериков?

public class FooReceiver {
  public void receive(Foo foo) {

  }
}

public interface Foo {
}

public class A implements Foo {
}

public class B implements Foo {
}

public class TestedClass {
  private FooReceiver receiver;
  public TestedClass(FooReceiver receiver) {
    this.receiver = receiver;
  }

  public void doStuff() {
    receiver.receive(new A());
    receiver.receive(new B());
  }
}

public class MyTest {

  @Test
  public void testingStuff() {
    // Setup
    FooReceiver mocked = Mockito.mock(FooReceiver.class);
    TestedClass t = new TestedClass(mocked);

    // Method under test
    t.doStuff();

    // Verify
    ArgumentCaptor<B> captor = ArgumentCaptor.forClass(B.class);
    Mockito.verify(mocked).receive(captor.capture()); // Fails here

    Assert.assertTrue("What happened?", captor.getValue() instanceof B);
  }
}

РЕДАКТИРОВАТЬ: Для всех, кто заинтересовался, я закончил делать это:

// Verify
final B[] b = new B[1];
ArgumentMatcher<B> filter = new ArgumentMatcher<B>() {
  @Override
  public boolean matches(Object argument) {
    if(argument instanceof B) {
      b[0] = (B) argument;
      return true;
    }
    return false;
  }
}
Mockito.verify(mocked).receive(Mockito.argThat(filter));

Ответы [ 3 ]

7 голосов
/ 11 июля 2012

Вы также можете использовать Mockito.isA, чтобы проверить, что аргумент имеет определенный класс:

verify(mock).init(isA(ExpectedClass.class));

Mockito JavaDoc

3 голосов
/ 08 января 2018

Насколько я могу судить, это ограничение / плохая реализация.Если посмотреть на org.mockito.internal.matchers.CapturingMatcher, то есть

public boolean matches(Object argument) {
    return true;
}

, что означает, что он соответствует каждому аргументу / классу.

В результате org.mockito.internal.matchers.CapturingMatcher#getAllValues возвращает List<B>, но фактически содержит один A иодин B, приводящий к ClassCastException во время выполнения при попытке получить их как B.

List<Object> arguments; // the invocations

// adds a new invocation
public void captureFrom(Object argument) {
    // ... 
    this.arguments.add(argument);
    // ... 
}

// return the list of arguments, using raw types remove any compiler checks for validity,
// the returned List contains elements that are not of type T
public List<T> getAllValues() {
    // ... 
    return new ArrayList<T>((List) arguments);
    // ... 
}

Это должно быть решено путем изменения org.mockito.ArgumentCaptor таким образом, чтобы оно передавало свой Class<? extends T> clazzв CapturingMatcher и, следовательно, надлежащим образом передают информацию о типе, обеспечивая правильную реализацию matches и устраняя необходимость использования типа cast / raw.

1 голос
/ 23 марта 2011

Метод будет вызван дважды, поэтому вам нужно сделать следующее:

Mockito.verify(mocked, times(2)).receive(captor.capture());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...