Based on modern testcontainers integration testing advanced road

Large-scale software projects in addition to a large number of essential production code there are a lot of automated testing. Automated testing comprises front to back even to various types of testing environments and the product line of different modules. A more classic theory of automated testing is to test the distribution of the pyramid, is that in a normal project should be a reasonable number of tests Unit Testing> Component Testing> Integration Testing> end test (system test)> manual verification test. This theory is generally justified because the unit test <Test Component <integration testing <-end test (test system) <From manual verification test complexity and test execution time code, so we should of course be to allocate more time and effort to understand and easy to perform a quick test to go, such as unit testing. Of course, these tests on the classification and definition of divergent views, such as component testing and integration testing, and sometimes even end testing, integration testing are not and will be called, because they are trying to test the system in two modules or between different system levels integration status.

The most classic example of integration testing should be a back-end systems integration testing between the application layer and the data layer of the bar. Data layer may be a conventional database, it may also be the new favorite Kafka Stream. Usually this integration testing several ideas:

  1. Deployed to the staging environment and then sends a request to the test system A, that request will contain the data read and write operations on the system B layer. This is actually considered skipping integration testing to end test. But this idea a lot of drawbacks, test code complexity is high, low path coverage from write bug to bug detected cycle is very long, is not the ideal solution.

  2. In tests using In-memory Embedded Database (usually memory-implemented version of the actual database system B mainly for such a test environment), so that the system can be refined to a test module which A X B is a database write operation, and can be written locally, run, debug, and the above solution than has been greatly improved. However, this solution still has several drawbacks:

  3. 很多In-memory Embedded Database只提供一个特定版本的实现,比如MongoDB 3.2,但如果你的实际数据库版本是4.0,那么很多新的数据库功能在测试里根本覆盖不了。
    有些In-memory Embedded Database甚至没有实现100%的接口兼容,或者不一样的实现方式,比如关系型数据库的transaction实现。这意为着就算你的测试过了,线上的代码还是可能会出错。这是常见的生产环境和测试环境不一致性问题。

受益于Docker的普及化,testcontainers提供了另外一种更为友好的集成测试解决方案。简单地讲就是在测试环境中动态创建需要的依赖服务的容器,比如动态创建一个Mongo 3.6的容器、创建一个RabbitMQ 最新发布版的容器,然后在测试中配置测试环境让测试应用使用创建好的容器暴露的可调用地址,测试结束后把使用过的容器销毁防止依赖服务状态迁移导致其他的测试莫名地挂掉。

这种解决方案有以下几个优点:

  • 每个Test Group都能像写单元测试那样细粒度地写集成测试,保证每个集成单元的高测试覆盖率
  • Test Group间是做到依赖隔离的,也就是说它们不共享任何一个Docker容器;假如两个Test Group都要用到Mongo 4.0,会创建两个容器供它们单独使用
  • 保证了生产环境和测试环境的一致性,代码部署到线上时不会遇到因为依赖服务接口不兼容而导致的bug
  • Test Group可以并行化运行,减少整体测试运行时间。相比较有些in-memory 的依赖服务实现没有实现很好的资源隔离,比如端口,一旦并行化运行就会出现端口冲突。
  • 得益于Docker,所有测试都可以在本地环境和CI/CD环境中运行,测试代码调试和编写就如同写单元测试

当然,它也有几个劣势:

  • 测试运行时间长:因为每个Test Group需要动态创建和销毁Docker容器,这两个步骤很多时候占用了大部分测试运行时间。当然客观地讲,这个等待时间还是秒级别的,所以还是能接受的。如果你再并行运行测试,总体运行时间还是可控的。
  • 测试编写、调试体验因为上面一点而受到影响
  • 资源占用率高:大部分的build agent都是一个虚拟机,甚至是一个docker进程,再加上还要给每个Test Group分配资源跑它们的依赖服务,整个build agent的CPU、内存使用率都会增加不少。在繁忙的时候甚至出现性能退化问题。解决方法就是scale up/out build agent。

从编程语言支持度来说,目前testcotainers的github org上提供了Java, Scala, Go, Rust, NodeJs, Python, C#的类库。从成熟度来说肯定是Java的类库最为成熟,已被不少开源项目使用。其他语言的类库可以想象不可避免会有些坑需要踩。

举一个官网的例子来说明如何使用testcontainers类库:

public class RedisBackedCacheIntTestStep0 {
    private RedisBackedCache underTest;

    @Before
    public void setUp() {
        // Assume that we have Redis running locally?
        underTest = new RedisBackedCache("localhost", 6379);
    }

    @Test
    public void testSimplePutAndGet() {
        underTest.put("test", "example");

        String retrieved = underTest.get("test");
        assertEquals("example", retrieved);
    }
}

上面的JUnit测试中动态创建了一个redis:5.0.3-alphine容器,在setUp方法里获取该容器的公开地址和接口从而创建我们要测试的RedisBackedCache实例,然后在测试里轻轻松地调用该实例的方法、验证结果。

testcontainers Java 提供了几个现成的使用频率较高的容器的类封装,比如大部分数据库(MySQL, Postgres, Cassandra, Neo4j), UI测试的Webdriver,ElasticSearch,Kafka, Nginx等等。如果你没找到现成的封装,你总是可以调用更底层的GenericContainer。它也支持主流的Java测试框架,JUnit4, JUnit 5, TestNG,Spock。总的来说对于写Java的同学这个类库使用起来还是非常爽的!

END

彩蛋福利

免费获取Java学习笔记,面试,文档以及视频

部分资料如下:

Guess you like

Origin blog.csdn.net/woshinidadaye_/article/details/91980365