J'essaye d'espionner un objet et je veux écraser une méthode qui est appelée par le constructeur avant que le constructeur l'appelle.
Ma classe ressemble à ça:
public class MyClass {
public MyClass() {
setup();
}
public void setup() {
}
}
La méthode de configuration ne doit pas être appelée. Eh bien, comment puis-je espionner cette méthode (et la configuration du stub pour qu'elle ne fasse rien)?
Cela fonctionne très bien avec la méthode moqueuse, mais je veux faire un test unitaire MyClass
et j'aurai donc besoin d'une autre méthode.
La raison pour laquelle il faut bloquer la méthode de configuration afin qu'elle ne fasse rien:
Je programme un robot Lego (lejos) et j'ai mis du code dans la configuration dont le robot a besoin pour fonctionner. Cependant, lorsque je l'appelle en dehors de TinyVM (le VM qui est installé sur le robot), Java se bloque car il VM n'a pas été correctement initialisé (car les tests s'exécutent sur mon PC). Pour les tests unitaires, la configuration n'est pas importante.
Je ne peux pas bloquer les appels de configuration des classes/méthodes car certains d'entre eux sont des variables finales statiques publiques.
Merci pour les suggestions, mais c'était un peu trop complexe.
J'ai fini par me moquer de la méthode en étendant la classe et en écrasant ma méthode de configuration. De cette façon, le constructeur par défaut n'appellera pas son implémentation de setup, il appellera la méthode remplacée à la place.
Voici le code:
// src/author/MyClass.Java
public class MyClass {
public MyClass() {
setup();
}
protected void setup() {
throw new Exception("I hate unit testing !");
}
public boolean doesItWork() {
return true;
}
}
// test/author/MyClass.Java
public class MyClassTest {
private class MockedMyClass extends MyClass {
@Override
protected void setup() {
}
}
private MyClass instance;
@Before
public void setUp() { // Not to be confusing with `MyClass#setup()`!
instance = new MockedMyClass();
}
@Test
public void test_doesItWork() {
assertTrue(instance.doesItWork());
}
}
Si vous ne voulez pas que la méthode de configuration de MyTest soit appelée ou écrasée par d'autres sous-classes, à l'exception de votre test (car un autre développeur peut très bien gâcher les choses en utilisant la méthode de configuration), changez simplement la visibilité par défaut et seules vos classes pourront pour appeler la configuration.
S'il existe un moyen plus simple, veuillez répondre à la question car je ne suis pas satisfait à 100% de ma solution.
Pour répondre directement à votre question, vous ne pouvez pas utiliser Mockito pour écraser une méthode appelée depuis le constructeur . Mockito a besoin d'une instance de la classe avant de pouvoir commencer à se moquer, et vous ne vous êtes pas donné le moyen de créer une instance pour le test.
Plus généralement, comme mentionné dans Java efficace élément 17, vous ne devez pas appeler de méthodes remplaçables par les constructeurs . Si vous le faites, par exemple, vous pouvez fournir une substitution dans une sous-classe qui fait référence à un champ final
mais qui s'exécute avant que le champ final
ne soit défini. Cela ne vous causera probablement pas de problèmes ici, mais c'est une mauvaise habitude en Java.
Heureusement, vous pouvez restructurer votre code pour le faire très facilement:
public class MyClass {
public MyClass() {
this(true);
}
/** For testing. */
MyClass(boolean runSetup) {
if (runSetup) {
setup();
}
}
/* ... */
}
Pour le rendre encore plus évident, vous pouvez rendre le constructeur à un paramètre MyClass
privé et fournir un public static
méthode d'usine:
/* ... */
public static MyClass createForTesting() {
return new MyClass(false);
}
private MyClass(boolean runSetup) {
/* ... */
Bien que certains développeurs pensent que c'est une mauvaise pratique d'écrire du code dans des méthodes utilisées principalement pour les tests, rappelez-vous que vous êtes responsable de la conception de votre code, et les tests sont l'un des rares consommateurs que vous savez absolument que vous devrez adapter . Bien que ce soit toujours une bonne idée d'éviter une configuration de test explicite dans le code de "production", la création de méthodes ou de surcharges supplémentaires pour le test rendra généralement votre code plus propre et peut considérablement améliorer la couverture et la lisibilité de votre test.
tilisez PowerMock.
Après avoir importé les bibliothèques, configurez-la pour manipuler la classe que vous souhaitez mocker avec la méthode d'instance qui ne doit pas être appelée.
Ainsi:
@RunWith(PowerMockRunner.class)
@PrepareForTest({<Other classes>, Myclass.class})
Ainsi:
suppress(method(Myclass.class, "setup"));
Ainsi:
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
// code here
return null;
}
}).when(Myclass.class, "setup");