J'ai une variable auto-câblée
@Test public void save_failure() { documentConfig.setNameRequired(true); /* testing code goes here */ } @After public void after() { documentConfig.setNameRequired(false); }
Je veux faire des tests pour le DocumentService avec différents états de cet objet de configuration. Quelles sont mes options? Quelle est la meilleure option?
La première idée est la suivante:
@Before public void after() { documentConfig.setNameRequired(true); } @Test public void save_failure() { /* testing code goes here */ } @After public void after() { documentConfig.setNameRequired(false); }
Mais je veux être un peu plus sûr que la variable est réinitialisée après le test pour ne pas interférer avec les autres tests, pour être sûr que seul ce test obtient une erreur s'il est à l'origine d'un problème.
Ma nouvelle idée était la suivante:
@Test public void save_failure() { documentConfig.setNameRequired(true); /* testing code goes here */ documentConfig.setNameRequired(false); }
Cependant, cela ne fonctionne pas du tout car Before et After s'exécutent pour tout le fichier et non pour ce test unique. Je préférerais ne pas créer un nouveau fichier juste pour un test.
J'ai maintenant décidé d'un compromis:
@Autowired private DocumentConfig documentConfig;
Il semble tout faire Je veux mais j'ai quelques questions.
En supposant que nameRequired
commence par false, cela garantit-il de ne pas interférer avec les autres tests?
Y a-t-il moyen de rendre cela plus clair? Tant pour mon futur moi que pour les autres.
3 Réponses :
Le cadre de test que vous utilisez n'est pas encore clair. Pour les tests unitaires simples, rendez la valeur injectable par un setter ou une injection de constructeur. Tout ce qui convient le mieux à votre situation spécifique.
S'il y a beaucoup (plus de trois ;-)) de telles valeurs à injecter, vous pouvez envisager d'introduire une classe de configuration pour injecter toutes ces valeurs en un seul paramètre.
Vous pouvez le créer avant chaque test. Smth comme
private DocumentConfig documentConfig; @Before public void createConfig() { documentConfig = new DocumentConfig(mockedParams); }
J'ai l'impression de comprendre cette réponse et je l'aime bien. Mais @After ne serait-il pas meilleur que @Before? Après tout, l'objectif est de conserver la configuration réinitialisée à tout moment, sauf pour ce test particulier. Avec un changement comme celui-ci, c'est en fait très similaire à ma propre solution ultime. EDIT: J'aurais alors également besoin d'assigner la variable dans la définition, semble-t-il.
Tant pis, je ne comprends pas. Cela ne semble pas connecter le documentConfig
dans ce fichier avec le documentConfig
autowired dans le DocumentService (l'objet principal de ces tests) et d'autres fichiers.
De plus: je suppose que vous avez parlé de new DocumentConfig (mockedParams)
, est-ce correct?
Une approche souvent utilisée consiste à configurer un DocumentConfig
factice et à l'injecter dans la méthode setUp ()
(annotée avec @Before
) afin que tout le contexte soit réinitialisé dans chaque test, par exemple:
@RunWith(MockitoJUnitRunner.class) public class DocumentServiceTest { @InjectMocks private DocumentService documentService; @Mock private DocumentConfig documentConfig; @Test public void save_failure() { when(this.documentConfig.isNameRequired()).thenReturn(true); // TODO: Implement test } }
Dans ce cas, j'ai configuré un objet simple avec nameRequired
étant faux
. Je pourrais probablement supprimer cette déclaration, car un champ booléen
par défaut est false
de toute façon.
Si vous n'utilisez pas d'injection de constructeur, et que vous ne le faites pas ' t avoir un setter pour documentConfig
, vous devrez utiliser la réflexion pour injecter le champ, par exemple:
@Before public void setUp() { // Use a static import for Mockito.mock() this.documentConfig = mock(DocumentConfig.class); this.service = new DocumentService(this.documentConfig); } @Test public void save_failure() { // Use a static import for Mockito.when() when(this.documentConfig.isNameRequired()).thenReturn(true); // TODO: Implement test }
Dans votre test, vous pouvez maintenant écrivez quelque chose comme ceci:
@Test public void save_failure() { this.documentConfig.setNameRequired(true); // TODO: Implement test }
Sinon, vous pouvez vous moquer de DocumentConfig
, afin de ne pas vous fier à son implémentation pour tester DocumentService . Je suppose que vous appelez isNameRequired ()
quelque part dans le code de DocumentService
, vous pouvez donc vous en moquer comme ceci:
ReflectionTestUtils.setField(this.service, "documentConfig", this.documentConfig);
Que testez-vous? DocumentConfig, ou une autre classe qui dépend de DocumentConfig? Si c'est le premier, utilisez try / finally. Dans ce dernier cas, simulez DocumentConfig.