Je suis passé récemment sur du dév. de batchs via Spring Batch et j'essaye de tester une Tasklet en bouchonnant les DAO fournis.
Je vais essayer de vous définir au mieux mon problème ainsi que mes approches actuelles, mais tout extrait de code que je pourrais vous fournir ne sera que reformulation de code et non du code d'origine (projet pro, secret pro, etc...).
Objectif
Lors de tests unitaires avec jUnit-Mockito, il faudrait bouchonner le(s) DAO injecté(s) par Spring Batch dans une tasklet.
Première approche (et unique, actuellement)
J'ai tenté de mocker le DAO dans ma classe de test, mais celle-ci ne se retrouve mocké que dans ma classe de test. Ci-dessous, mes classes impactés, ré-écrites sans données sensibles.
Ma classe de test :
/**
* Test unitaire pour la {@link Tasklet} {@link TotoTasklet}.
*
* @author chinto
* @version 1.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:jobs/Toto-Job.xml" })
public class TotoTaskletTest {
/**
* La tasklet à tester unitairement.
*/
@InjectMocks
private TotoTasklet tasklet;
/**
* Instance de l'interface {@link TotoDAO}, à bouchonner.
*/
@Mock
private final TotoJDBCTemplate totoDao = new TotoJDBCTemplate();
/**
* Le résultat de la liste retournée par le DAO
*/
private final List<Toto> listeToto = new ArrayList<Toto>();
/**
* Méthode d'initialisation des tests.
*
* @throws Exception
*/
@Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
initListeToto();
}
/**
* Méthode de définition pour le bouchonnage de la méthode {@link TotoDAO#lister()}.
*/
private void initListeToto() {
final Toto t1 = new Toto();
listeToto.add(t1);
final Toto T2 = new Toto();
listeToto.add(T2);
Mockito.when(totoDao.lister()).thenReturn(listeToto);
}
/**
* Méthode de test pour
* {@link TotoTasklet#execute(org.springframework.batch.core.StepContribution, org.springframework.batch.core.scope.context.ChunkContext)}.
*
* @throws Exception
*/
@Test
@DirtiesContext
public void testExecute() throws Exception {
final JobExecution jobExecution = jobLauncherTestUtils.launchStep("TotoStep");
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
}
}
Et la Tasklet :
/**
* Tasklet Toto.
*
* @author chinto
* @version 1.0
*/
public class TotoTasklet implements Tasklet {
/** Logger pour mettre la log dans les fichiers logs **/
private static final Logger LOGGER = LoggerFactory.getLogger(TotoTasklet.class);
/** accès à la table TOTO **/
@Inject
private TotoDAO totoDao;
/** Liste des toto. **/
private List<Toto> listeToto;
/**
* Corps de la {@link Tasklet}.
*
* @param contribution
* mutable state to be passed back to update the current step execution (viens de la Javadoc officielle de {@link Tasklet}).
* @param chunkContext
* le contexte de la {@link Tasklet}.
* @throws Exception
* Aucune exception n'est interceptée lors du process.
*/
@Override
public RepeatStatus execute(final StepContribution contribution, final ChunkContext chunkContext) throws Exception {
populateListeToto();
return RepeatStatus.FINISHED;
}
/**
* Rempli la {@link List liste} des toto à partir de la table des {@link Toto}.
*/
private void populateListeToto() {
listeToto = totoDao.listerAboRefbic();
}
}
Malgré le fait que la tasklet que je défini dans le test utilise bien mon DAO, je n'arrive pas à définir que Spring doit utiliser cette tasklet...
Merci d'avance pour vos retours.
- Edité par chinto 26 mai 2016 à 11:39:03
Mes amis, la patience est un arbre aux racinesamères, mais aux fruits si doux...
Je ne suis pas certain de la réponse, que je n'ai que vaguement croisé Mockito, mais je pense avoir une idée.
Si j'ai bien compris, c'est que TotoTasklet::totoDao utilise un DAO "normal", bien que tu ais voulu le mocker dans ton test TotoTaskletTest ?
Spring va initialiser les @Beans du context et les @Injecter dans les classes qu'il rencontre. Sauf qu'ici Spring n'a aucune connaissance de ton attribut TotoTasklet::totoDao : tu as beau le mocker dans ton coin, si tu ne dis pas à Spring de l'utiliser alors il va toujours utiliser celui "par défaut".
Une solution serait de définir un @Bean ayant une plus grande priorité par rapport au TotoDAO original : soit un fichier de config, soit via @Order ou @Primary :
@Configuration
class MyMocks {
@Bean
@Primary // ou @Order(...)
TotoDAO totoDAO() {
// To mock ici ton @Bean
return Mockito.mock(TotoDAO.class);
}
}
Angular 2 est l'avenir, jQuery c'est de la merde !!! - Java 8 c'est l'an 2016+ (programmez en 1 ligne)
Si j'ai bien compris, c'est que TotoTasklet::totoDao utilise un DAO "normal", bien que tu ais voulu le mocker dans ton test TotoTaskletTest ?
C'est exactement l'idée. Le DAO dans ma Tasklet est initié par défaut par la moulinette de Spring et est ensuite injecté dans TotoTasklet::totoDao grâce à l'attribut @Inject.
Pinguet62 a écrit:
Spring va initialiser les @Beans du context et les @Injecter dans les classes qu'il rencontre.
Sauf qu'ici Spring n'a aucune connaissance de ton attribut TotoTasklet::totoDao : tu as beau le mocker dans ton coin, si tu ne dis pas à Spring de l'utiliser alors il va toujours utiliser celui "par défaut".
Je me doutais que c'était un problème de ce genre, mais je n'arrivais pas à trouver comment demander à Spring de l'utiliser
Pinguet62 a écrit:
Une solution serait de définir un @Bean ayant une plus grande priorité par rapport au TotoDAO original : soit un fichier de config, soit via @Order ou @Primary :
@Configuration
class MyMocks {
@Bean
@Primary // ou @Order(...)
TotoDAO totoDAO() {
// To mock ici ton @Bean
return Mockito.mock(TotoDAO.class);
}
}
Je teste cette solution (via les annotations Configuration et Primary) et je reviens ici pour dire ce qu'il en est.
- Edité par chinto 26 mai 2016 à 13:56:57
Mes amis, la patience est un arbre aux racinesamères, mais aux fruits si doux...
Ça marche parfaitement avec cette classe de configuration. Merci pour ton aide.
Sujet résolu
Mes amis, la patience est un arbre aux racinesamères, mais aux fruits si doux...
[Java EE][Spring Batch][Mockito] Bouchonnage DAO
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.