J'aimerais tester une classe abstraite. Bien sûr, je peux écrire manuellement un simulateur qui hérite de la classe.
Puis-je le faire en utilisant un framework de mocking (j'utilise Mockito) au lieu de créer manuellement mon mock ? Comment ?
J'aimerais tester une classe abstraite. Bien sûr, je peux écrire manuellement un simulateur qui hérite de la classe.
Puis-je le faire en utilisant un framework de mocking (j'utilise Mockito) au lieu de créer manuellement mon mock ? Comment ?
La suggestion suivante vous permet de tester des classes abstraites sans créer de sous-classe "réelle" - le Mock est la sous-classe.
utiliser Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS)
puis simule toutes les méthodes abstraites qui sont invoquées.
Exemple :
public abstract class My {
public Result methodUnderTest() { ... }
protected abstract void methodIDontCareAbout();
}
public class MyTest {
@Test
public void shouldFailOnNullIdentifiers() {
My my = Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS);
Assert.assertSomething(my.methodUnderTest());
}
}
Note : L'avantage de cette solution est que vous n'avez pas besoin de ont pour implémenter les méthodes abstraites, tant qu'elles ne sont jamais invoquées.
À mon avis, c'est plus propre que d'utiliser un espion, car un espion nécessite une instance, ce qui signifie que vous devez créer une sous-classe instanciable de votre classe abstraite.
+1 pour m'avoir aidé à résoudre mon problème qui n'était pas lié à la question. Le CALLS_REAL_METHODS était utile pour injecter un objet stub dans mon SUT. Un stub était plus logique qu'un mock car les valeurs de retour étaient un peu complexes.
Comme indiqué ci-dessous, cela ne fonctionne pas lorsque la classe abstraite appelle des méthodes abstraites afin d'être testée, ce qui est souvent le cas.
Cela fonctionne en fait lorsque la classe abstraite appelle des méthodes abstraites. Il suffit d'utiliser la syntaxe doReturn ou doNothing au lieu de Mockito.when pour stubber les méthodes abstraites, et si vous stubbez des appels concrets, assurez-vous de stubber les appels abstraits en premier.
Si vous avez juste besoin de tester certaines des méthodes concrètes sans toucher aux abstractions, vous pouvez utiliser CALLS_REAL_METHODS
(voir Réponse de Morten ), mais si la méthode concrète testée appelle certaines des méthodes abstraites, ou des méthodes d'interface non implémentées, cela ne fonctionnera pas -- Mockito se plaindra "Cannot call real method on java interface".
(Oui, c'est une conception médiocre, mais certains frameworks, par exemple Tapestry 4, vous l'imposent en quelque sorte).
La solution de contournement consiste à inverser cette approche - utiliser le comportement ordinaire de l'objet fantaisie (c.-à-d. que tout est fantaisie/stubé) et utiliser la fonction doCallRealMethod()
de mentionner explicitement la méthode de bétonnage à l'essai. Par exemple
public abstract class MyClass {
@SomeDependencyInjectionOrSomething
public abstract MyDependency getDependency();
public void myMethod() {
MyDependency dep = getDependency();
dep.doSomething();
}
}
public class MyClassTest {
@Test
public void myMethodDoesSomethingWithDependency() {
MyDependency theDependency = mock(MyDependency.class);
MyClass myInstance = mock(MyClass.class);
// can't do this with CALLS_REAL_METHODS
when(myInstance.getDependency()).thenReturn(theDependency);
doCallRealMethod().when(myInstance).myMethod();
myInstance.myMethod();
verify(theDependency, times(1)).doSomething();
}
}
Mis à jour pour ajouter :
Pour les méthodes non vides, vous devrez utiliser [thenCallRealMethod()
](http://docs.mockito.googlecode.com/hg/org/mockito/stubbing/OngoingStubbing.html#thenCallRealMethod()) à la place, par exemple :
when(myInstance.myNonVoidMethod(someArgument)).thenCallRealMethod();
Sinon, Mockito se plaindra "Unfinished stubbing detected".
Cela fonctionnera dans certains cas, mais Mockito n'appelle pas le constructeur de la classe abstraite sous-jacente avec cette méthode. Cela peut faire échouer la "vraie méthode" en raison de la création d'un scénario inattendu. Ainsi, cette méthode ne fonctionnera pas non plus dans tous les cas.
Oui, vous ne pouvez pas du tout compter sur l'état de l'objet, seulement sur le code de la méthode appelée.
Vous pouvez y parvenir en utilisant un espion (utilisez toutefois la dernière version de Mockito 1.8+).
public abstract class MyAbstract {
public String concrete() {
return abstractMethod();
}
public abstract String abstractMethod();
}
public class MyAbstractImpl extends MyAbstract {
public String abstractMethod() {
return null;
}
}
// your test code below
MyAbstractImpl abstractImpl = spy(new MyAbstractImpl());
doReturn("Blah").when(abstractImpl).abstractMethod();
assertTrue("Blah".equals(abstractImpl.concrete()));
Les frameworks de simulation sont conçus pour faciliter la simulation des dépendances de la classe que vous testez. Lorsque vous utilisez un framework de mocking pour simuler une classe, la plupart des frameworks créent dynamiquement une sous-classe et remplacent l'implémentation de la méthode par du code permettant de détecter l'appel d'une méthode et de renvoyer une fausse valeur.
Lorsque vous testez une classe abstraite, vous voulez exécuter les méthodes non abstraites de l'objet testé (SUT), donc un framework de mocking n'est pas ce que vous voulez.
Une partie de la confusion vient du fait que la réponse à la question à laquelle vous avez accédé par un lien dit de créer à la main une classe fantaisie qui s'étend à partir de votre classe abstraite. Je n'appellerais pas une telle classe un mock. Une classe fantaisie est une classe qui est utilisée pour remplacer une dépendance, qui est programmée avec des attentes et qui peut être interrogée pour voir si ces attentes sont satisfaites.
Au lieu de cela, je suggère de définir une sous-classe non-abstraite de votre classe abstraite dans votre test. Si cela entraîne trop de code, cela peut être un signe que votre classe est difficile à étendre.
Une solution alternative serait de rendre votre scénario de test lui-même abstrait, avec une méthode abstraite pour créer le SUT (en d'autres termes, le scénario de test utiliserait la fonction Méthode du modèle modèle de conception).
Essayez d'utiliser une réponse personnalisée.
Par exemple :
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class CustomAnswer implements Answer<Object> {
public Object answer(InvocationOnMock invocation) throws Throwable {
Answer<Object> answer = null;
if (isAbstract(invocation.getMethod().getModifiers())) {
answer = Mockito.RETURNS_DEFAULTS;
} else {
answer = Mockito.CALLS_REAL_METHODS;
}
return answer.answer(invocation);
}
}
Il renverra la méthode fantaisie pour les méthodes abstraites et appellera la méthode réelle pour les méthodes concrètes.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
6 votes
A partir de Mockito 1.10.12 Mockito prend en charge l'espionnage/le moquage des classes abstraites directement :
SomeAbstract spy = spy(SomeAbstract.class);
15 votes
Depuis la version 2.7.14 de Mockito, vous pouvez également simuler les classes abstraites qui nécessitent des arguments de constructeur par l'intermédiaire de
mock(MyAbstractClass.class, withSettings().useConstructor(arg1, arg2).defaultAnswer(CALLS_REAL_METHODS))