Pular para o conteúdo principal

Testando Seus Handlers

Um TaskHandler/AnswerProvider/DueDateProvider é uma classe Java comum — na maioria dos casos, ele não precisa do motor rodando para ser testado. Reserve testes com o motor real para verificar o fluxo do processo (roteamento, variáveis se propagando entre nós), não a lógica interna de cada handler.

Caminho recomendado: teste unitário isolado

Mocke ExecutionContext e teste o handler como testaria qualquer outra classe:

@Test
void lancaErroDeNegocioQuandoCpfInvalido() {
ExecutionContext execution = mock(ExecutionContext.class);
when(execution.getVariable("cpf"))
.thenReturn(new ProcessVariable("cpf", "cpf-invalido"));

CalcularScoreTaskHandler handler = new CalcularScoreTaskHandler(mock(BureauClient.class));

assertThrows(ProcessErrorException.class, () -> handler.handle(execution));
}

Sem Spring, sem motor, sem I/O — roda em milissegundos e é o lugar certo para cobrir as ramificações de lógica de negócio do handler (casos de borda, validações, cálculos — a fórmula do score, aqui).

Teste de integração: o fluxo completo com o motor real

Quando você quer verificar que um processo inteiro se comporta como esperado — o roteamento de um gateway, a propagação de variáveis entre nós, o efeito de completar uma tarefa externa — suba um @SpringBootTest real trocando a persistência de produção pela implementação in-memory:

<dependency>
<groupId>io.kikwiflow</groupId>
<artifactId>kikwi-in-memory-spring-boot-starter</artifactId>
<version>${kikwiflow.version}</version>
<scope>test</scope>
</dependency>
@SpringBootTest
class AberturaDeContaProcessoIT {

@Autowired
private KikwiflowEngine kikwiflowEngine;

@Test
void concluiAposAnalistaAprovar() {
ProcessInstance instancia = kikwiflowEngine.startProcess()
.byKey("abertura-de-conta")
.withBusinessKey("111.111.111-11")
.withVariables(Map.of(
"cpf", new ProcessVariable("cpf", "111.111.111-11"),
"rendaDeclarada", new ProcessVariable("rendaDeclarada", 8000)))
.execute();

// Calcular Score é síncrona; a instância já está parada em "Aprovar Abertura".
assertEquals(ProcessInstanceStatus.ACTIVE, instancia.status());

kikwiflowEngine.completeExternalTask(
findPendingTaskId(instancia, "Aprovar Abertura"),
Map.of("decisao", new ProcessVariable("decisao", "APROVADA")),
IdentityContext.system());

ProcessInstance concluida = kikwiflowEngine.findProcessInstanceById(instancia.id());
assertEquals(ProcessInstanceStatus.COMPLETED, concluida.status());
}
}

kikwi-in-memory-spring-boot-starter registra um KikwiEngineRepository in-memory automaticamente (via @ConditionalOnMissingBean), então basta ele estar no classpath de teste — nenhuma configuração adicional é necessária, e nenhum Mongo real é necessário para este teste.

Nós assíncronos (commitBefore: true) exigem esperar o poller

Diferente do caminho totalmente síncrono do exemplo acima, um nó com commitBefore: true é retomado por um worker interno que faz polling periódico (kikwiflow.execution.task-acquisition-interval-millis, default 5 segundos) — em um @SpringBootTest real esse poller está de fato rodando, então o teste precisa esperar (por exemplo, com Awaitility) até a instância alcançar o status esperado, em vez de assumir que o resultado já está pronto assim que .execute() retorna. É o caso do ramo Consulta Bureau de Processamento Paralelo.

Assertions mais expressivas (opcional)

Se quiser um vocabulário de asserção mais rico do que inspecionar ProcessInstance na mão (assertThatProcessInstanceIsActive, assertHasActiveExternalTaskOn, etc.), adicione kikwi-core-testing como dependência de teste e substitua o bean de repositório pela sua AssertableKikwiEngine:

<dependency>
<groupId>io.kikwiflow</groupId>
<artifactId>kikwi-core-testing</artifactId>
<version>${kikwiflow.version}</version>
<scope>test</scope>
</dependency>
@TestConfiguration
class AssertableEngineTestConfig {
@Bean
@Primary
KikwiEngineRepository assertableKikwiEngine() {
return new AssertableKikwiEngine();
}
}
@SpringBootTest
@Import(AssertableEngineTestConfig.class)
class AberturaDeContaAssertableIT {

@Autowired
private KikwiflowEngine kikwiflowEngine;

@Autowired
private AssertableKikwiEngine assertableKikwiEngine;

@Test
void paraEmAprovarAberturaAposCalcularScore() {
kikwiflowEngine.startProcess()
.byKey("abertura-de-conta")
.withBusinessKey("BK-TEST-2")
.withVariables(Map.of(
"cpf", new ProcessVariable("cpf", "222.222.222-22"),
"rendaDeclarada", new ProcessVariable("rendaDeclarada", 9000)))
.execute();

assertableKikwiEngine.assertHasActiveExternalTaskOn("Aprovar Abertura");
}
}

AssertableKikwiEngine implementa KikwiEngineRepository diretamente — como Spring escolhe o bean marcado @Primary, o resto da fiação (KikwiflowEngine, Navigator, ContinuationService) passa a operar sobre ele sem qualquer outra mudança de configuração.

Este caminho hoje é manual, não um recurso pronto

Não existe (ainda) um @KikwiflowTest/test slice do Spring Boot que faça essa troca de bean automaticamente — o @TestConfiguration acima é curto, mas é algo que você mesmo declara no seu projeto.

Quando usar / quando não usar

Use teste unitário para toda regra de negócio dentro de um handler — é rápido, e é onde os casos de borda cabem.

Use teste de integração com o motor para verificar o fluxo: um gateway roteando para o lado certo, uma variável chegando no nó seguinte, uma tarefa externa liberando a continuação. Não o use para reexercitar a lógica que o teste unitário já cobre.

Próximo passo

Com os handlers testados, o processo básico está completo. A partir daqui a trilha entra em tópicos avançados — o primeiro é rodar passos independentes ao mesmo tempo, em Processamento Paralelo.