Axon 框架:如何在单元测试中检索状态存储聚合 ID 的 ID

Axon framework: How to retrieve id of a state-stored aggregate id in a unit test

我是 Axon 框架的新手,正在尝试使用 CQRS 和状态存储聚合来实现应用程序。聚合是通过命令处理构造函数创建的,该构造函数分配一个随机 UUID 作为聚合标识符。

为了在聚合上测试命令,我通过 givenCommands(new CreatePlanCommand(...) 调用创建了聚合。然后我想发出被测命令,在本例中为 UpdatePlanNameCommand,但该命令需要由在测试设置中执行的 CreatePlanCommand 分配的聚合 ID。有办法找回吗?

示例测试代码如下所示(请参阅 when() 调用评论中的问题):

@Test
public void plan_updatePlan_updatesPlanName() {
    testFixture
            .givenCommands(new CreatePlanCommand(123L, "My Test Plan", funds))
            .when(new UpdatePlanNameCommand(/* How to get aggregate id returned by CreatePlanCommand here? */, "New Name"))
            .expectSuccessfulHandlerExecution()
            .expectState(state -> {
                assertThat(state.getCompanyId(), equalTo(123L));
                assertThat(state.getName(), equalTo("New Name"));
                assertThat(state.getAvailableFunds(), equalTo(funds));
            });
}

创建计划的命令处理程序如下:

@CommandHandler
public Plan(CreatePlanCommand command, PlanFundValidator planFundValidator) {
    // Use injected domain service to verify that all funds in this plan's lineup actually exist
    planFundValidator.validateFundsExist(command.getAvailableFunds());

    this.id = UUID.randomUUID();
    this.companyId = command.getCompanyId();
    this.name = command.getName();
    this.availableFunds = new HashSet<>(command.getAvailableFunds());
    apply(planCreatedEvent());
}

更新:我能够通过使用 .givenState(...) 创建聚合来使测试正常工作,如下所示:

@Test
public void plan_updatePlan_updatesPlanName() {
    AtomicReference<UUID> planId = new AtomicReference<>();
    testFixture
            .givenState(() -> {
                Plan plan = new Plan(new CreatePlanCommand(123L, "My Test Plan", funds), mockPlanFundValidator);
                planId.set(plan.getId());
                return plan;
            })
            .when(new UpdatePlanNameCommand(planId.get(), "New Name"))
            .expectSuccessfulHandlerExecution()
            .expectState(state -> {
                assertThat(state.getCompanyId(), equalTo(123L));
                assertThat(state.getName(), equalTo("New Name"));
                assertThat(state.getAvailableFunds(), equalTo(funds));
            });
}

但这似乎过于冗长并且在测试可读性和维护方面倒退了一步,所以我仍然很好奇是否有一种方法可以用 .givenCommands(...) 来实现这一点。谢谢。

好吧,让我试着给你一些信息。

在我看来,实现它最简单的方法是将UUID生成移动到另一个Component。这样,您就可以在测试中清楚地模拟它。

例如:

@CommandHandler
public Plan(CreatePlanCommand command, PlanFundValidator planFundValidator, UUIDGenerator generator) {
    // Use injected domain service to verify that all funds in this plan's lineup actually exist
    planFundValidator.validateFundsExist(command.getAvailableFunds());

    this.id = generator.generate(); // changed this line
    this.companyId = command.getCompanyId();
    this.name = command.getName();
    this.availableFunds = new HashSet<>(command.getAvailableFunds());
    apply(planCreatedEvent());
}

这样做,就像你模拟 PlanFundValidator 一样,你也模拟了 UUIDGenerator 以确保你拥有所需的 ID。

另一个问题:

Related question: is there a way to clear any events that were raised in the given part of a test? For example, if I have a unit test for command "C" that executes commands "A" and "B" as part of its "given" to set up the test, I would like to be able to assert that command "C" (the command under test) raised the expected events and not see any events raised by commands "A" and "B" since those are not the subject of this test and have their own tests that verify they raise the expected events.

事件不会在测试之间保留,但如果这些事件是由您自己的测试引发的,则您必须处理它们。通过 givenState 你之前发现的也可以帮助解决这个问题。您只需设置您需要的状态并触发您正在测试的 command/event。