Использование Mockito для моделирования классов с общими параметрами


280

Есть ли чистый метод насмешки над классом с общими параметрами? Скажем, я должен смоделировать класс, Foo<T>который мне нужно передать в метод, который ожидает Foo<Bar>. Я могу сделать следующее достаточно легко:

Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());

Предполагая, getValue()возвращает универсальный тип T. Но это будет иметь котят, когда я позже передам это в метод ожидания Foo<Bar>. Является ли кастинг единственным средством сделать это?

Ответы:


280

Я думаю, вам нужно разыграть его, но это не должно быть слишком плохо:

Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue()).thenReturn(new Bar());

34
Да, но у вас все еще есть предупреждение. Можно ли избежать предупреждения?
— odwl

12
@SuppressWarnings ("unchecked")
— квалификационный выпуск

18
Я думаю, что это вполне приемлемо, поскольку речь идет о фиктивном объекте в модульном тесте.
— Magnilex

1
@demaniak Это не работает вообще. Сопоставители аргументов не могут быть использованы в этом контексте.
— Кшиштоф Красонь

1
@demaniak Это прекрасно скомпилируется, но при запуске теста выдает InvalidUseOfMatchersException (который является RuntimeException)
— Superole

277

Еще один способ обойти это - использовать @Mockаннотацию. Не работает во всех случаях, но выглядит намного сексуальнее :)

Вот пример:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

    @Mock
    public Foo<Bar> fooMock;

    @Test
    public void testFoo() {
        when(fooMock.getValue()).thenReturn(new Bar());
    }
}

MockitoJUnitRunnerИнициализирует поля аннотированные с @Mock.


3
это устарело в 1.9.5. :( Мне кажется намного чище.
— Код Novitiate

12
@CodeNovitiate Я не смог найти аннотации к устареванию на MockitoJUnitRunner и Mock в 1.9.5. Итак, что является устаревшим? (Да, org.mockito.MockitoAnnotations.Mock устарела, но вместо этого следует использовать org.mockito.Mock)
— neu242

12
Хорошо, это отлично сработало для меня. Он не просто «сексуальнее», он избегает предупреждения без использования SuppressWarnings. Предупреждения существуют по причине, лучше не иметь привычки подавлять их. Спасибо!
— Николь

4
Есть одна вещь, которая мне не нравится использовать @Mockвместо mock(): поля все еще являются пустыми во время построения, поэтому я не могу вставлять зависимости в это время и не могу сделать поля окончательными. Первый может быть решен с помощью @Beforeаннотированного метода, конечно.
— Рюдигер Шульц

3
Для инициации просто вызовите MockitoAnnotations.initMocks (this);
— Борхаб

42

Вы всегда можете создать промежуточный класс / интерфейс, который будет удовлетворять универсальному типу, который вы хотите указать. Например, если Foo был интерфейсом, вы можете создать следующий интерфейс в своем тестовом классе.

private interface FooBar extends Foo<Bar>
{
}

В ситуациях, когда Foo не является финальным классом, вы можете просто расширить класс следующим кодом и сделать то же самое:

public class FooBar extends Foo<Bar>
{
}

Тогда вы можете использовать любой из приведенных выше примеров с помощью следующего кода:

Foo<Bar> mockFoo = mock(FooBar.class);
when(mockFoo.getValue()).thenReturn(new Bar());

4
При условии, Fooчто это интерфейс или не финальный класс, это представляется довольно элегантным решением. Спасибо.
— Тим Клемонс

Я обновил ответ, добавив примеры и для нефинальных классов. В идеале вы должны писать на интерфейсе, но это не всегда так. Хороший улов!
— Дсинглтон

16

Создайте метод тестовой утилиты . Особенно полезно, если вам это нужно более одного раза.

@Test
public void testMyTest() {
    // ...
    Foo<Bar> mockFooBar = mockFoo();
    when(mockFooBar.getValue).thenReturn(new Bar());

    Foo<Baz> mockFooBaz = mockFoo();
    when(mockFooBaz.getValue).thenReturn(new Baz());

    Foo<Qux> mockFooQux = mockFoo();
    when(mockFooQux.getValue).thenReturn(new Qux());
    // ...
}

@SuppressWarnings("unchecked") // still needed :( but just once :)
private <T> Foo<T> mockFoo() {
    return mock(Foo.class);
}

Можно расширить ваш ответ, чтобы сделать общий метод утилит, передаваемый в классе, который вы хотите смоделировать.
— Уильям Даттон

1
@WilliamDutton static <T> T genericMock(Class<? super T> classToMock) { return (T)mock(classToMock); }это даже не нуждается в единственном подавлении :) Но будьте осторожны, Integer num = genericMock(Number.class)компилируйте, но бросайте ClassCastException. Это полезно только для наиболее распространенного G<P> mock = mock(G.class)случая.
— TWiStErRob

6

Я согласен с тем, что не следует подавлять предупреждения в классах или методах, поскольку можно пропустить другие, случайно подавленные предупреждения. Но ИМХО абсолютно разумно подавить предупреждение, которое затрагивает только одну строку кода.

@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);

3

Вот интересный случай: метод получает универсальную коллекцию и возвращает универсальную коллекцию того же базового типа. Например:

Collection<? extends Assertion> map(Collection<? extends Assertion> assertions);

Этот метод может быть смоделирован с помощью сочетания Mockito anyCollectionOf и Ответа.

when(mockedObject.map(anyCollectionOf(Assertion.class))).thenAnswer(
     new Answer<Collection<Assertion>>() {
         @Override
         public Collection<Assertion> answer(InvocationOnMock invocation) throws Throwable {
             return new ArrayList<Assertion>();
         }
     });
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.