02/10/2018
Introduction
Cet article explique comment exécuter un fichier Drools à partir d’une classe de tests, en mockant les classes de service.
Sources
Un projet exemple est disponible dans le projet flower-extras.
Dans ce projet, on teste unitairement un handler basé sur la réponse à une tâche.
Les tests unitaires sont situés dans la classe TaskAnswerTest
.
Import du fichier Drools
Le code suivant permet de charger le fichier FileCheckAtCreationTest.xls
et de le renvoyer lorsque FlowerDocs va chercher le fichier Drools.
public DocumentFile buildRulesFile(String filename) throws URISyntaxException
{
DocumentFile file = new DocumentFile();
URL url = ClassLoader.getSystemResource("Drools/" + filename);
file.setContent(new DataHandler(new FileDataSource(new File(url.toURI()))));
return file;
}
@Before
public void setUpContext() throws TechnicalException, FunctionalException, URISyntaxException
{
when(documentService.getFiles(any(Id.class), anyBoolean()))
.thenReturn(toList(buildRulesFile("FileCheckAtCreationTest.xls")));
}
Mock des services
Les classes de services sont mockées via un objet Java avec l’annotation @Configuration
, une méthode avec l’annotation @Bean
et en utilisant la méthode mock
de Mockito comme ci-dessous :
@Configuration
public class ServiceConfigTest
{
(...)
@Bean
DocumentService documentService()
{
return mock(DocumentService.class);
}
(...)
}
Pour les classes non mockées, la procédure est presque identique, on appelle le constructeur dans la méthode :
@Import({ CacheConfiguration.class, InternalServicesConfiguration.class, ServiceConfigTest.class })
protected static class AbstractDroolsHandlerConfig
{
(...)
@Bean
DroolsOperationHandler droolsOperationHandler()
{
return new DroolsOperationHandler();
}
(...)
}
Dans le code ci-dessous, les différents mocks sont injectés dans la classe DroolsOperationHandler
.
La récupération avec @Autowired de l’objet documentService permet de récupérer l’object mocké.
@Autowired
@InjectMocks
protected DroolsOperationHandler operationHandler;
@Autowired
protected DocumentService documentService;
(...)
Mockito.when(documentService.(...)).thenReturn(...);
(...)
Exécution du handler
Pré-requis
- Le fichier Drools a été chargé
Un contexte a été fourni pour authentification (voir méthode ci-dessous)
private void setUpCredentials() { AuthenticatedUser user = new SpringPrincipalUser(); user.setId(new Id("user")); user.setScope(new Id("GEC")); SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, null)); } @Before public void setUp() throws TechnicalException, FunctionalException, URISyntaxException { MockitoAnnotations.initMocks(this); setUpCredentials(); (...) }
Spécificité liée au changement de classe
Par exemple sur une classe de tâches, si on a une réponse qui :
- met à jour une propriété
Service
avec une valeurService 1
- met à jour la classe de
Tache à traiter
àTâche en traitement
Si la propriété Service
n’est pas définie sur la classe de tâches Tâche en traitement
, on ne pourra pas tester si la propriété Service
a été correctement renseignée.
when(taskClass.getTagReferences()).thenReturn(ComponentBuilder.initTagsReference(Lists.newArrayList("Service")));
Construction de l’objet OperationContext
L’exécution du handler prend en entrée un élément de type OperationContext
, qui dépend du type de handler testé.
Ce contexte est ensuite récupérable via le fichier Drools. L’objet à construire dépend donc fortement du fichier Drools testé.
Ci-dessous quelques exemples de contextes utilisés :
//Update document content
private UpdateContentOperationContext buildComponentContext(String fileName)
{
UpdateContentOperationContext updateContentContext = new UpdateContentOperationContext();
updateContentContext.setComponent(ComponentBuilder.buildDocument(fileName));
IdentifiableElement documentFile = ComponentBuilder.buildFile(fileName);
List<IdentifiableElement> content = Lists.newArrayList(documentFile);
updateContentContext.setContent(content);
updateContentContext.setRegistration(new Id("CONTEXT"));
return updateContentContext;
}
public TaskOperationContext buildTaskContext(Task task)
{
List<Component> components = new ArrayList<Component>();
components.add(task);
TaskOperationContext taskContext = new TaskOperationContext();
taskContext.setComponents(components);
taskContext.setRegistration(new Id("CONTEXT"));
return taskContext;
}
Appel du handler
Le handler est appelé grâce à la méthode process
, qui prend en entrée le contexte créé précédemment.
Dans l’exemple ci-dessous, on simule une création de tâche (réponse Initiate
).
Le handler récupère la valeur du tag g_destinataire
et lui assigne la tâche.
Ci-dessous le test permettant de vérifier si la tâche est bien affectée à l’utilisateur :
@Test
public void should_assign_at_creation_if_receiver_tag_is_filled() throws Exception
{
Task task = ComponentBuilder.createTask("Initiate");
task.getTags().getTags().add(TagBuilder.name("g_destinataire").value("USER").build());
task.setAnswer(ComponentBuilder.answer("Initiate"));
operationHandler.process(buildTaskContext(task));
assertEquals("USER", task.getAssignee());
}