首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >与事件采购系统的集成测试

与事件采购系统的集成测试
EN

Stack Overflow用户
提问于 2019-05-10 04:44:22
回答 2查看 1.1K关注 0票数 2

我正在开发一个PoC,我们将CQRS与事件源结合使用。我们使用Axon框架和Axon服务器作为工具集。

我们有一些微服务(Maven包)和一些业务逻辑。

应用程序流程的简单概述:

我们将一条xml消息(带有REST)发布到服务1,这将导致一个事件(带有聚合)。服务2处理由服务1“触发”的事件,并启动一个saga流。例如,sage流的一部分是发送邮件消息。

我可以用Axon Test做一些测试,以测试来自服务1的聚合或来自服务2的saga。但是,有没有一个好的选择来做一个真正的集成测试,我们从向REST接口发布消息开始,并检查聚合和saga中的所有操作(包括发送邮件等)?

也许这种集成测试做得有点过头了,最好是单独测试每个组件。我怀疑测试这种类型的系统需要什么/最好的解决方案。

EN

回答 2

Stack Overflow用户

发布于 2019-05-10 05:59:25

我建议看看测试容器(https://www.testcontainers.org/)

它提供了一种在JUnit测试中启动和干净利落地拆除docker容器的非常方便的方法。这个特性对于针对真实数据库和任何其他资源(例如Axon Server)进行应用程序集成测试非常有用,这些资源可以使用docker镜像(https://hub.docker.com/r/axoniq/axonserver/)。

我分享了一些来自JUnit 4测试类(Kotlin)的代码片段。希望这可以帮助您开始并发展您特定的测试策略(集成应该涵盖较小的范围,而不是端到端的测试)。我的观点是,集成测试应该分别/独立地关注Axon消息传递API组件和REST API组件。端到端应该覆盖微服务中的所有组件。

代码语言:javascript
复制
@RunWith(SpringRunner::class)
@SpringBootTest
@ContextConfiguration(initializers = [DrestaurantCourierCommandMicroServiceIT.Initializer::class])
internal class DrestaurantCourierCommandMicroServiceIT {

    @Autowired
    lateinit var eventStore: EventStore

    @Autowired
    lateinit var commandGateway: CommandGateway

    companion object {

        // An Axon Server container
        @ClassRule
        @JvmField
        var axonServerTestContainer = KGenericContainer(
                "axoniq/axonserver")
                .withExposedPorts(8024, 8124)
                .waitingFor(Wait.forHttp("/actuator/info").forPort(8024))
                .withStartupTimeout(Duration.of(60L, ChronoUnit.SECONDS))

        // A PostgreSQL container is being started up using a JUnit Class Rule which gets triggered before any of the tests are run:

        @ClassRule
        @JvmField
        var postgreSQLContainer = KPostgreSQLContainer(
                "postgres:latest")
                .withDatabaseName("drestaurant")
                .withUsername("demouser")
                .withPassword("thepassword")
                .withStartupTimeout(Duration.of(60L, ChronoUnit.SECONDS))
    }

    // Pass details on the application as properties BEFORE Spring starts creating a test context for the test to run in:
    class Initializer : ApplicationContextInitializer<ConfigurableApplicationContext> {

        override fun initialize(configurableApplicationContext: ConfigurableApplicationContext) {
            val values = TestPropertyValues.of(
                    "spring.datasource.url=" + postgreSQLContainer.jdbcUrl,
                    "spring.datasource.username=" + postgreSQLContainer.username,
                    "spring.datasource.password=" + postgreSQLContainer.password,
                    "spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect",
                    "axon.axonserver.servers=" + axonServerTestContainer.containerIpAddress + ":" + axonServerTestContainer.getMappedPort(8124)
            )
            values.applyTo(configurableApplicationContext)
        }
    }


    @Test
    fun `restaurant command microservice integration test - happy scenario`() {

        val who = "johndoe"
        val auditEntry = AuditEntry(who, Calendar.getInstance().time)
        val maxNumberOfActiveOrders = 5
        val name = PersonName("Ivan", "Dugalic")
        val orderId = CourierOrderId("orderId")

        // ******* Sending the `createCourierCommand` ***********
        val createCourierCommand = CreateCourierCommand(name, maxNumberOfActiveOrders, auditEntry)
        commandGateway.sendAndWait<Any>(createCourierCommand)
        await withPollInterval org.awaitility.Duration.ONE_SECOND atMost org.awaitility.Duration.FIVE_SECONDS untilAsserted {
            val latestCourierCreatedEvent = eventStore.readEvents(createCourierCommand.targetAggregateIdentifier.identifier).asStream().toList().last().payload as CourierCreatedEvent
            assertThat(latestCourierCreatedEvent.name).isEqualTo(createCourierCommand.name)
            assertThat(latestCourierCreatedEvent.auditEntry.who).isEqualTo(createCourierCommand.auditEntry.who)
            assertThat(latestCourierCreatedEvent.maxNumberOfActiveOrders).isEqualTo(createCourierCommand.maxNumberOfActiveOrders)
        }

        // ******* Sending the `createCourierOrderCommand` **********
        val createCourierOrderCommand = CreateCourierOrderCommand(orderId, auditEntry)
        commandGateway.sendAndWait<Any>(createCourierOrderCommand)
        await withPollInterval org.awaitility.Duration.ONE_SECOND atMost org.awaitility.Duration.FIVE_SECONDS untilAsserted {
            val latestCourierOrderCreatedEvent = eventStore.readEvents(createCourierOrderCommand.targetAggregateIdentifier.identifier).asStream().toList().last().payload as CourierOrderCreatedEvent
            assertThat(latestCourierOrderCreatedEvent.aggregateIdentifier.identifier).isEqualTo(createCourierOrderCommand.targetAggregateIdentifier.identifier)
            assertThat(latestCourierOrderCreatedEvent.auditEntry.who).isEqualTo(createCourierOrderCommand.auditEntry.who)
        }

        // ******* Assign the courier order to courier **********
        val assignCourierOrderToCourierCommand = AssignCourierOrderToCourierCommand(orderId, createCourierCommand.targetAggregateIdentifier, auditEntry)
        commandGateway.sendAndWait<Any>(assignCourierOrderToCourierCommand)
        await withPollInterval org.awaitility.Duration.ONE_SECOND atMost org.awaitility.Duration.FIVE_SECONDS untilAsserted {
            val latestCourierOrderAssignedEvent = eventStore.readEvents(assignCourierOrderToCourierCommand.targetAggregateIdentifier.identifier).asStream().toList().last().payload as CourierOrderAssignedEvent
            assertThat(latestCourierOrderAssignedEvent.aggregateIdentifier.identifier).isEqualTo(assignCourierOrderToCourierCommand.targetAggregateIdentifier.identifier)
            assertThat(latestCourierOrderAssignedEvent.auditEntry.who).isEqualTo(assignCourierOrderToCourierCommand.auditEntry.who)
            assertThat(latestCourierOrderAssignedEvent.courierId.identifier).isEqualTo(assignCourierOrderToCourierCommand.courierId.identifier)
        }

    }
}

class KGenericContainer(imageName: String) : GenericContainer<KGenericContainer>(imageName)
class KPostgreSQLContainer(imageName: String) : PostgreSQLContainer<KPostgreSQLContainer>(imageName)
票数 7
EN

Stack Overflow用户

发布于 2020-08-08 01:23:21

Axon开发人员建议使用here中提到的docker解决方案。Testcontainers在这里似乎是最好的。我的java代码片段:

代码语言:javascript
复制
@ActiveProfiles("test")
public class TestContainers {

    private static final int AXON_HTTP_PORT = 8024;
    private static final int AXON_GRPC_PORT = 8124;

    public static void startAxonServer() {
        GenericContainer axonServer = new GenericContainer("axoniq/axonserver:latest")
                .withExposedPorts(AXON_HTTP_PORT, AXON_GRPC_PORT)
                .waitingFor(
                        Wait.forLogMessage(".*Started AxonServer.*", 1)
                );
        axonServer.start();

        System.setProperty("ENV_AXON_GRPC_PORT", String.valueOf(axonServer.getMappedPort(AXON_GRPC_PORT)));
    }

@BeforeClass中调用startAxonServer方法。现在,您必须获取外部docker端口(在withExposedPorts中指示的这些端口是内部docker端口)。您可以在运行时通过getMappedPort完成此操作,如我的代码片段所示。请记住为您的测试套件提供连接配置。我在spring boot上的例子如下:

代码语言:javascript
复制
axon:
  axonserver:
    servers: localhost:${ENV_AXON_GRPC_PORT}

完整的工作解决方案可以在我的github project上找到。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56067305

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档