web-dev-qa-db-fra.com

Utiliser Mockito pour simuler des classes avec des paramètres génériques

Existe-t-il une méthode propre pour se moquer d'une classe avec des paramètres génériques? Disons que je dois me moquer d'une classe Foo<T> dont j'ai besoin pour passer dans une méthode qui attend un Foo<Bar>. Je peux faire assez facilement ce qui suit:

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

En supposant que getValue() renvoie le type générique T. Mais cela va avoir des chatons quand je le passerai plus tard dans une méthode en attente de Foo<Bar>. Est-ce que le casting est le seul moyen de le faire?

230
Tim Clemons

Je pense que vous avez besoin de le lancer, mais ça ne devrait pas être trop grave:

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

Une autre solution consiste à utiliser l'annotation @Mock à la place… .. Ne fonctionne pas dans tous les cas, mais semble beaucoup plus sexy :)

Voici un exemple:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

    @Mock
    public Foo<Bar> fooMock;

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

Le MockitoJUnitRunner initialise les champs annotés avec @Mock .

233
Marek Kirejczyk

Vous pouvez toujours créer une classe/interface intermédiaire répondant au type générique que vous souhaitez spécifier. Par exemple, si Foo était une interface, vous pouvez créer l'interface suivante dans votre classe de test.

private interface FooBar extends Foo<Bar>
{
}

Dans les situations où Foo est une classe non-finale , vous pouvez simplement étendre la classe avec le code suivant et faire la même chose:

public class FooBar extends Foo<Bar>
{
}

Ensuite, vous pouvez utiliser l'un des exemples ci-dessus avec le code suivant:

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

Créez une méthode utilitaire test . Spécialement utile si vous en avez besoin plus d'une fois.

@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);
}
12
acdcjunior

Voici un cas intéressant: une méthode reçoit une collection générique et retourne une collection générique du même type de base. Par exemple:

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

Cette méthode peut être simulée avec la combinaison de Mockito anyCollectionOf matcher et de 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>();
         }
     });
3
qza

Je conviens qu'il ne faut pas supprimer les avertissements dans les classes ou les méthodes, car on pourrait négliger d'autres avertissements supprimés accidentellement. Mais à mon humble avis, il est tout à fait raisonnable de supprimer un avertissement qui n’affecte qu’une seule ligne de code.

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