如何在Saga中测试具有内部状态的方法

时间:2019-02-25 10:44:32

标签: axon

给出了这个传奇:

@Saga
@Getter
@Slf4j
public class TasksForStateSaga {

    @Autowired
    transient CommandGateway commandGateway;

    @Autowired
    transient EventBus eventBus;

    @Autowired
    transient TaskService taskService;

    Map<String, TaskStatus> tasks = new HashMap<>();

    ApplicationState applicationState;

    @StartSaga
    @SagaEventHandler(associationProperty = "id")
    public void on(ApplicationStateChangedEvent event) {
        applicationState = event.getNewState();
        log.info("Planning tasks for application {} in state {}", event.getId(), applicationState);
        taskService.getTasksByState(applicationState).stream()
                .map(task -> ScheduleTaskCommand.builder()
                        .applicationId(event.getId())
                        .taskId(IdentifierFactory.getInstance().generateIdentifier())
                        .targetState(applicationState)
                        .taskName(task.getTaskName())
                        .build())
                .peek(command -> tasks.put(command.getTaskId(), SCHEDULED))
                .forEach(command -> commandGateway.send(command));
    }

    @SagaEventHandler(associationProperty = "applicationId")
    public void on(TaskFinishedEvent event) {
        tasks.replace(event.getTaskId(), FINISHED);
        long notFinished = getUnfinishedCount();

        log.info("Task {} has just finished, ready {} of {}", event.getTaskName(), tasks.size() - notFinished, tasks.size());

        if (notFinished == 0) {
            log.info("All tasks for application {}.{} finished, ending this saga", event.getApplicationId(), applicationState);
            eventBus.publish(GenericEventMessage.asEventMessage(
                    TaskForStateDoneEvent.builder()
                            .applicationId(event.getApplicationId())
                            .state(applicationState)
                            .build()
            ));
            SagaLifecycle.end();
        }
    }

    private long getUnfinishedCount() {
        return tasks.values().stream()
                .filter(state -> !FINISHED.equals(state))
                .count();
    }
}

让此测试(Spock)测试第一种方法:

class TasksForStateSagaTest extends Specification {

SagaTestFixture sagaFixture

def setup() {
    sagaFixture = new SagaTestFixture<>(TasksForStateSaga)
}

def 'should schedule task for the application state'() {
    given:
    def applicationId = '1'
    def taskService = Mock(TaskService)
    def tasks = [
            ApplicationStateAwareTaskDefinition.builder().taskName('task1').build(),
            ApplicationStateAwareTaskDefinition.builder().taskName('task2').build(),
    ]
    sagaFixture.registerResource(taskService)
    sagaFixture.givenAggregate(applicationId)

    when:
    sagaFixture
            .whenPublishingA(new ApplicationStateChangedEvent(id: applicationId, newState: ApplicationState.NEW))
            .expectActiveSagas(1)
            .expectDispatchedCommandsMatching(payloadsMatching(
            exactSequenceOf(
                    equalTo(new ScheduleTaskCommand(applicationId: applicationId, targetState: ApplicationState.NEW, taskName: 'task1'),
                            new IgnoreField(ScheduleTaskCommand, 'taskId')),
                    equalTo(new ScheduleTaskCommand(applicationId: applicationId, targetState: ApplicationState.NEW, taskName: 'task2'),
                            new IgnoreField(ScheduleTaskCommand, 'taskId')),
                    andNoMore()
            )
    ))

    then:
    1 * taskService.getTasksByState(ApplicationState.NEW) >> tasks

}

}

但是我实际上不知道如何测试使用Saga内部状态的第二种方法。

有人可以建议我如何通过SagaTestFixture设置内部传奇状态吗? 甚至更多,这是实现此类传奇的好方法,还是我遇到了一些概念上的问题,使我无法轻松测试终结传奇方法?

@StartSaga方法设置内部状态-生成taskId并将其设置为map @EndSaga方法读取地图并在发送TaskForStateDoneEvent事件之前检查所有任务是否已完成

谢谢!

1 个答案:

答案 0 :(得分:1)

我通过为saga装置定义回调实现了这一点。该回调捕获所需类型的所有命令,并临时存储ids

def 'saga should end when all scheduled tasks finished'() {
    given:
    def applicationId = '1'
    def taskService = Mock(TaskService)
    def tasks = [
            ApplicationStateAwareTaskDefinition.builder().taskName('task1').build(),
            ApplicationStateAwareTaskDefinition.builder().taskName('task2').build(),
    ]
    def scheduleTaskCommands = []

    sagaFixture.registerResource(taskService)
    sagaFixture.setCallbackBehavior(new CallbackBehavior() {
        @Override
        Object handle(Object commandPayload, MetaData commandMetaData) {
            if (commandPayload instanceof ScheduleTaskCommand) {
                // catch the issued commands to get the new task ids
                scheduleTaskCommands << commandPayload
            }

            commandPayload
        }
    })

    when:
    sagaFixture
            .givenAggregate(applicationId)
            .published(new ApplicationStateChangedEvent(
            applicationId: applicationId,
            newState: ApplicationState.NEW
    ))
    // publish event to finish first task (first from two)
    sagaFixture.givenAPublished(new TaskFinishedEvent(
            taskName: 'someTaskName',
            applicationId: applicationId,
            taskId: scheduleTaskCommands[0].taskId,
            taskResult: new TaskResult(
                    status: TaskResultStatus.SUCCEED,
                    message: 'ok'
            )))

    // verify the behaviour after second task finished
    sagaFixture
            .whenPublishingA(new TaskFinishedEvent(
            taskName: 'someTaskName',
            applicationId: applicationId,
            taskId: scheduleTaskCommands[1].taskId,
            taskResult: new TaskResult(
                    status: TaskResultStatus.SUCCEED,
                    message: 'ok'
            )))
            .expectActiveSagas(0)
            .expectPublishedEvents(new TasksForStateFinishedEvent(
            applicationId: applicationId,
            state: ApplicationState.NEW
    ))

    then:
    1 * taskService.getTasksByState(ApplicationState.NEW) >> tasks
}