Використання Mockito для макетування класів із загальними параметрами


280

Чи існує чистий метод глузування з класом із загальними параметрами? Скажіть, я маю знущатися над класом, Foo<T>який мені потрібно передати в метод, який очікує на a 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 ("невірно")
кваліфікаційний

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. :( Здається мені набагато чистішим.
Code Novitiate

12
@CodeNovitiate Я не зміг знайти жодних анотацій щодо депресації на MockitoJUnitRunner та Mock в 1.9.5. Отже, що застаріло? (Так, org.mockito.MockitoAnnotations.Mock застарілий, але замість цього слід використовувати org.mockito.Mock)
neu242

12
Молодці, це прекрасно працювало для мене. Це не просто "сексуальніше", він уникає попередження без використання SuppressWarnings. Попередження існують з причини, краще не мати звички їх придушувати. Дякую!
Ніколь

4
Є одне, що мені не подобається використовувати @Mockзамість mock(): поля все ще є недійсними протягом часу побудови, тому я не можу вставляти залежності в цей час і не можу зробити поля остаточними. Перший можна вирішити, @Beforeзвичайно, методом, що коментується.
Rüdiger Schulz

3
Для ініціації просто зателефонуйте MockitoAnnotations.initMocks (це);
borjab

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інтерфейс чи не завершальний клас, це представляється досить елегантним рішенням. Дякую.
Тім Клемонс

Я оновив відповідь, щоб включити приклади і для позафінальних занять. В ідеалі ви б кодували інтерфейс, але це не завжди так. Гарний улов!
dsingleton

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

Я погоджуюся, що не слід придушувати попередження в класах чи методах, оскільки можна було не помітити інших, випадково придушених попереджень. Але IMHO абсолютно розумно придушити попередження, яке стосується лише одного рядка коду.

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

3

Ось цікавий випадок: метод отримує загальну колекцію та повертає загальну колекцію того ж базового типу. Наприклад:

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

Цей метод можна знущатись, поєднуючи Mockito anyCollectionOf matcher та Answer.

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.