创建一个堆栈java_如何使用Testcontainer创建简单的多堆栈测试

Testcontainers是一个轻量级库,用于简化在本地环境中测试多个集成服务的过程。通过封装Docker并与测试框架如Junit集成,它允许工程师在IDE中直接运行测试,创建一次性服务实例,确保每个测试的独立性和速度。本文通过一个Java和Kafka的例子,展示了如何使用Testcontainers创建和管理测试中的服务堆栈。

创建一个堆栈java

如今的后端工程可能需要集成多种服务。 对于工程师来说,在他们的本地开发环境中安装许多服务是很痛苦的。 Docker提供了一种更简单的方法来执行此操作,但是它仍然需要在我们的代码之外进行一些脚本编写。 如果我们要测试较小的函数或类而不是整个服务,那么它也不是一个完美的解决方案。 这个问题已由Testcontainers [ 1 ]解决。

本文将使您了解什么是Testcontainer,为什么要使用Testcontainers,最后是如何使用它。 尽管本文仅基于Java编程语言的经验,但Testcontainers确实支持其他编程语言,例如go [ 2 ]和node-js [ 3 ]。

什么是测试容器

Testcontainers是一个轻量级的库,可以“包装” docker并连接到测试框架,例如Junit和Spock。 如您在其maven存储库中所见,它也是模块化的。 这意味着,您只能导入对您重要的必要堆栈。

为什么要使用测试容器

测试容器肯定会使软件工程师的生活更简单!

Docker和Docker Compose简化了在本地开发和生产环境中的部署服务。 它们就像轻量级虚拟机,因此您在当前环境中所做的任何操作都不会影响Docker环境中包含的服务。 但是,您需要告诉docker在当前打开的IDE之外运行服务。 使用Tescontainer将使此过程变得更短。 您可以直接在Intellij IDEA中使用ctrl + F10。

使Testcontainers发光的另一个有趣的方面是,它将创建一次性实例。 这意味着您可以在假设每个测试可以独立执行的前提下编写测试。 您可能不想在一项测试中保留插入PostgreSQL数据库中的任何内容。 保留测试记录可能会导致另一个测试失败。 这导致您的测试出现错误。 等待! 您是否正在测试,以便您的主要代码没有错误?

测试容器将使我们能够在一个测试类中使用多个技术堆栈。 例如,您需要测试确实插入到Mysql实例的Kafka使用者。 我们可以在同一个类中定义这两种服务!

如何使用测试容器

尽管Testcontainers可以在Junit运行时中运行,但最好在集成测试中使用。 通常,您希望在几百毫秒内运行一个单元测试类。 在单元测试中运行许多Testcontainer实例将导致单元测试非常慢,并且您不希望每个单元测试运行约20秒。

只需几行

假设您要使用Kafka,可以在测试文件中轻松地将Kafka实例定义为

public class JavaKafkaTest  {
    @Rule
    public KafkaContainer kafka;

    @Before
    public void setUp () throws Exception  {
        kafka = new KafkaContainer( "5.4.2" );
        kafka.start();
    }
}

此代码将以几行代码下载版本5.4.2 (Kafka版本2.5.0 )的Confluent平台的docker映像文件,并启动docker。

Kafka与zookeeper结合使用,但是默认情况下,它已由Testcontainers处理。 如果您出于某种原因希望使用自己的动物园管理员,那么幸运的是有一个选择。

完整示例:使用Kafka流生成消息并使用

我已经向您展示了如何制作KafkaContainer实例的一些知识,现在我将演示完整的课程。

public class JavaKafkaTest  {

    @Rule
    public KafkaContainer kafka;

    @Before
    public void setUp () throws Exception  {
        kafka = new KafkaContainer( "5.4.2" );
        kafka.start();
        Properties adminProperties = new Properties();
        adminProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getBootstrapServers());
        AdminClient adminClient = KafkaAdminClient.create(adminProperties);
        adminClient.createTopics(
            Stream.of( "plaintext-input" )
                .map(n -> new NewTopic(n, 1 , ( short ) 1 ))
                .collect(Collectors.toList())
        ).all().get();
    }

    @Test
    public void testKafkaTestcontainer () throws InterruptedException  {
        prepareSeedMessages();
        final Topology topology = prepareKafkaTopology();
        final KafkaStreams streams = new KafkaStreams(topology, consumerProps());
        streams.start();
        Thread.sleep( 5000 );


    }

    private void prepareSeedMessages ()  {
        KafkaProducer producer = new KafkaProducer(producerProps());
        producer.send( new ProducerRecord( "plaintext-input" , "this is sparta" ));
        producer.send( new ProducerRecord( "plaintext-input" , "this is sparta" ));
        producer.send( new ProducerRecord( "plaintext-input" , "this is sparta" ));
        producer.close();
    }

    private Topology prepareKafkaTopology ()  {

        final StreamsBuilder streamsBuilder = new StreamsBuilder();
        streamsBuilder
            .<String, String>stream( "plaintext-input" )
            .peek((k, v) -> {
                Logger log = Logger.getLogger(Thread.currentThread().getName());
                log.info(String.format( "receive message from plaintext-input : %s" , v));
            })
            .flatMapValues(v -> Arrays.asList(v.split( "\\W+" )))
            .peek((k, v) -> {
                Logger log = Logger.getLogger(Thread.currentThread().getName());
                log.info(String.format( "receive message from count : %s" , v));
            })
            .groupBy((k, v) -> v)
            .count();

        return streamsBuilder.build();
    }

    private Properties consumerProps ()  {
        Properties consumerProps = new Properties();
        consumerProps.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
        consumerProps.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
        consumerProps.put(StreamsConfig.APPLICATION_ID_CONFIG, "sample-app" );
        consumerProps.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getBootstrapServers());
        return consumerProps;
    }

    private Properties producerProps ()  {
        Properties producerProperties = new Properties();
        producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafka.getBootstrapServers());
        producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        return producerProperties;
    }

    @After
    public void tearDown ()  {
        kafka.stop();
    }

}

setUp方法上,准备了所有必需的组件。 必须先启动KafkaContainer ,然后再进行其他调用。 请记住, @Before将对每个测试方法都执行,这样会使我们的测试不可变。

但是另一方面,这将使容器在测试方法之间上下浮动。

函数testKafkaContainer是可运行的部分。 在这里您可以做事并断言您需要的所有东西。 本示例演示使用单个方法完成发布和订阅。

prepareSeedMessages过程将使用三个字符串填充Kafka队列。 我设置了标准的必需属性:密钥序列化程序,值序列化程序,应用程序ID和引导服务器。 重要的是,在设置生产者的属性时,我从Kafka容器实例中获取一个引导服务器 ,而不是自己对其进行硬编码。

然后,我们准备创建Kafka流的拓扑。 拓扑基本上是有向无环图(DAG),它定义了处理的顺序 [4]。 我将使用“必要的”字数拓扑示例。

在这里,我已定义为从“纯文本输入”主题中获取输入。 我使用peek执行记录过程,并返回该过程收到的内容。 然后, flatMapValues将使用空格将每个令牌断开。 然后,我可以将每个单词分组并进行计数。

最后,我使用tearDown方法停止了Kafka容器。 此方法将破坏容器并删除该容器内的所有数据。

如果在一个类中有多个测试,最好将start和stop方法移到@BeforeClass@AfterClass 。 像本例一样,使用@Before@After将导致在每次执行单个测试方法时创建和销毁容器。 这会使运行此测试类的时间很长。 相反,我们可以为每个测试类声明一次构造。

结论

在这里,我已经简要说明了Testcontainers是一个封装docker并使它与测试库一起运行的库。 我还列出了使用Testcontainers的一些原因:直接在您的IDE中运行并独立运行测试。

最后,我给出了在Testcontainers中使用Kafka的示例。

参考资料

翻译自: https://hackernoon.com/how-to-create-simple-multi-stacks-test-with-testcontainers-tx1u3uj5

创建一个堆栈java

vs2010 中添加 ActiveX Control Test Container工具(转载) vs2010中的TSTCON( ActiveX Control Test Container )工具非自动安装,而是作为一个例程提供。所以应找到该例程,并编译: 如vs2010安装在默认路径则 1, 进入:C:\Program Files\Microsoft Visual Studio 10.0\Samples\1033,可找到VC2010Samples.zip文件, 2, 将其解决到同一个文件夹,解压后将出现一个C++子文件夹。 3, TstCon例程在:C:\Program Files\Microsoft Visual Studio 10.0\Samples\1033\C++\MFC\ole\TstCon,双击其中的.sln文件启动工程,直接编译, 4, 编译后在Debug子文件夹中将出现一个TstCon.exe文件,将其复制到:C:\Program Files\Microsoft Visual Studio 10.0\Common7\Tools文件夹。 5, 在vs2010中的Tools菜单点击“External Tools...”,出现如下对话框,点击“Add”添加一项工具。 6, 新工具项的"Title"为:ActiveX Control Test Container,"Command"为:C:\Program Files\Microsoft Visual Studio 10.0\Common7\Tools\TstCon.exe。 7, 确认退出对话框,Tools菜单将出现“ActiveX Control Test Container”菜单项。完毕! 同理在2015安装目录下找到并编译: 但是编译的时候需要区分win32和x64的不同平台生成的工具 分别添加 ActiveX Control Test Container 32和 ActiveX Control Test Container 64的工具,因为不同工具显示的是不同平台注册的com组件,比如你注册一个32位的com组件,此时只能用ActiveX Control Test Container 32测试,而在ActiveX Control Test Container 64中是找不到这个com组件的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值