DevX 开源组件 DevXRoutingDataSource 单元测试篇

85 阅读10分钟

Beautiful girl natural beauty

DevX 开源组件 DevXRoutingDataSource 单元测试篇

我们做单元测试的目的是为了验证代码的正确性和稳定性,以及确保代码在修改后仍能够正常工作。通过编写单元测试,我们可以对代码进行逐一测试,发现其中的潜在问题和错误,并及时修复。这有助于提高代码的质量和可维护性,减少后期的调试和修复工作量,同时也有助于加速开发过程,提高开发效率。此外,单元测试也有助于促进代码重构和优化,使代码更加简洁、清晰、易于理解和维护。因此,单元测试是软件开发过程中非常重要的一环,可以帮助我们构建高质量、可靠、稳定和易于维护的软件系统。

本章会介绍在 DevXRoutingDataSource 中是如何进行单元测试的.为了提供高质量的开源产品我们会尽可能的对编写的代码进行充分的测试.

单元测试的原则

在开始编写单元测试前我们需要搞清楚编写单元测试的依据是什么,理论先行。

  1. 单一职责原则:每个测试用例应该只测试一个功能或一个方面,避免测试用例过于复杂和冗长。

  2. 边界条件原则:测试用例应该覆盖所有的边界条件和特殊情况,以确保代码的正确性和鲁棒性。

  3. 可重复性原则:测试用例应该是可重复执行的,即每次运行测试用例的结果应该一致。

  4. 独立性原则:测试用例之间应该是相互独立的,一个测试用例的失败不应该影响其他测试用例的执行。

  5. 可读性原则:测试用例应该易于阅读和理解,以便于其他开发人员或测试人员能够快速了解测试用例的目的和设计。

  6. 可维护性原则:测试用例应该易于维护和更新,以适应代码的变化和需求的变化。 以上原则都是为了确保测试用例的质量和有效性,以提高代码的可靠性和稳定性。

DevXRoutingDataSource 如何进行单元测试

去外部依赖 数据库选型

作为一款 SQL 路由产品,我们的代码是和关系型数据库强关联的,为了保证单元测试可以在任何机器轻松执行,无需任何的外部依赖(这是我们要遵守的核心原则和要达到的基本目标),我们选择使用 H2 内嵌数据库的内存模式,开发者无需在自己的机器上安装任何的关系型数据库产品,就可以很轻松的运行单元测试.

H2 是一个基于 Java 的关系型数据库管理系统(RDBMS)。它是一个轻量级的、嵌入式的数据库,可以作为独立应用程序或作为 Java 应用程序的一部分运行。H2 数据库支持 SQL 和 JDBC 标准,并提供了许多高级特性,如内存模式、加密、压缩、嵌入式模式、服务器模式等。H2 数据库的性能和可靠性都很好,支持事务和多用户并发访问,同时也具有良好的兼容性和可移植性。由于其轻量级和易于使用的特点,H2 数据库被广泛应用于开发和测试环境中,也可以用于小型应用程序和嵌入式设备中。

image.png

保证单元测试的独立性

DevXRoutingDataSource 中我们为每一个有必要的类编写了独立的单元测试,每一个单元测试的方法都具有独立性,为了保障这一点尤其是在对数据库中的数据进行了操作之后,要保障下一个运行的测试方法不受干扰,所有的行为都符合我们的预期.

使用了 junit5 , assertj 编写单元测试.

为了符合我们多数据源和读写分离的场景,我们在每个测试方法运行前会去在多个数据源上执行初始化 SQL 脚本 init.sql . 使用 @BeforeEach 注解让它在每个单元测试方法运行前执行. 使用 @AfterEach 注解在每个单元测试方法运行后清理测试现场,为了不影响到下一个单元测试的行为。

-- Create area table  
CREATE TABLE IF NOT EXISTS area (  
id BIGINT PRIMARY KEY,  
name VARCHAR(100) NOT NULL  
);  
  
-- Insert test data  
INSERT INTO area (id, name) VALUES  
(1, 'East'),  
(2, 'South');  
  
-- Create department table  
CREATE TABLE IF NOT EXISTS department (  
id BIGINT PRIMARY KEY,  
name VARCHAR(100) NOT NULL,  
area_id INT NOT NULL  
);  
  
-- Insert test data  
INSERT INTO department (id, name, area_id) VALUES  
(1, 'Research and Development', 1),  
(2, 'Sales', 2);  
  
-- Create employee table  
CREATE TABLE IF NOT EXISTS employee (  
id BIGINT PRIMARY KEY,  
name VARCHAR(100) NOT NULL,  
department_id INT NOT NULL  
);  
  
-- Insert test data  
INSERT INTO employee (id, name, department_id) VALUES  
(1, 'John Doe', 1),  
(2, 'Jane Doe', 1),  
(3, 'Bob Smith', 2),  
(4, 'Alice Jones', 2);  
  
-- Create salary table  
CREATE TABLE salary (  
employee_id INT PRIMARY KEY,  
amount DECIMAL(10, 2) NOT NULL,  
date DATE NOT NULL  
);  
  
-- Insert test data  
INSERT INTO salary (employee_id, amount, date) VALUES  
(1, 10000.00, '2023-05-01'),  
(2, 12000.00, '2023-05-01'),  
(3, 8000.00, '2023-05-01'),  
(4, 9000.00, '2023-05-01');
/**
 * @author Peng He
 * @since 1.0
 */

@TestPropertySource(locations = "classpath:application.yaml")
public class BeforeAfterEachHandleDataTest extends SpringBootIntegrationTest {

    @Autowired
    RoutingDataSource dataSource;

    @BeforeEach
    protected void initData() throws Exception {
        String initSqlPath = "src/test/resources/init.sql";

        DataSource write0 = dataSource.getDataSourceWithName("write_0");
        Connection write0Conn = write0.getConnection();
        ResultSet write0Rs = RunScript.execute(write0Conn , new FileReader(initSqlPath));
        close(write0Rs , null , write0Conn);

        DataSource read0 = dataSource.getDataSourceWithName("read_0");
        Connection read0Conn = read0.getConnection();
        ResultSet read0Rs = RunScript.execute(read0Conn , new FileReader(initSqlPath));
        close(read0Rs , null , read0Conn);

        DataSource read1 = dataSource.getDataSourceWithName("read_1");
        Connection read1Conn = read1.getConnection();
        ResultSet read1Rs = RunScript.execute(read1Conn , new FileReader(initSqlPath));
        close(read1Rs , null , read1Conn);
    }

    @AfterEach
    protected void clearData() throws Exception {

        RoutingContext.clear();
        DataSource write0 = dataSource.getDataSourceWithName("write_0");
        Connection write0Conn = write0.getConnection();
        Statement write0Stmt = write0Conn.createStatement();
        write0Stmt.execute("SHUTDOWN");


        DataSource read0 = dataSource.getDataSourceWithName("read_0");
        Connection read0Conn = read0.getConnection();
        Statement read0Stmt = read0Conn.createStatement();
        read0Stmt.execute("SHUTDOWN");

        DataSource read1 = dataSource.getDataSourceWithName("read_1");
        Connection read1Conn = read1.getConnection();
        Statement read1Stmt = read1Conn.createStatement();
        read1Stmt.execute("SHUTDOWN");
    }

    private static void close(ResultSet rs , Statement stmt , Connection conn) throws Exception {
        if (Objects.nonNull(rs)) {
            rs.close();
        }
        if (Objects.nonNull(stmt)) {
            stmt.close();
        }
        if (Objects.nonNull(conn)) {
            conn.close();
        }
    }

}

这里存在一个问题就是我们在这个代码中直接使用了 H2 的 API RunScript (执行 SQL 脚本) 和命令 SHUTDOWN (关闭数据库), 这样就导致了一个问题我们的单元测试代码和某一个实现有了强关联,会导致我们如果更换了数据库就要对代码进行相应的变更。这一点之后我们会进行优化。

与 Spring 集成测试

Spring 事务管理测试

考虑到 Spring 作为主流的 Java 语言开发框架,所以对与 Spring 集成工作也编写了单元测试. 例如测试读写事务场景, 使用 @Transactional 注解让单元测试方法在由 Spring 管理的事务中执行. 使用 @Commit 方法标识单元测试方法执行后事务需要提交 (默认单元测试事务方法执行结束后事务会回滚) 或使用 @Rollback(false) 注解也可以达到同样的目的. 为了保证单元测试方法的独立性使用 @DirtiesContext 注解标识,默认下一个单元测试方法执行前会创建一个新的 Spring 上下文容器。

/**
 * testing Transactional
 *
 * @author Peng He
 * @since 1.0
 */

@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class TransactionalTest extends BeforeAfterEachHandleDataTest {

    @Autowired
    EmployeeMapper employeeMapper;

    @Autowired
    DepartmentMapper departmentMapper;

    @Autowired
    AreaMapper areaMapper;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Transactional(rollbackFor = Throwable.class)
    @Commit
    @DirtiesContext
    @Test
    @Order(1)
    void testReadWriteTxFunc() {

        log.info("testing read write Tx");
        assertThat(RoutingContext.inTx()).isEqualTo(true);

        Map<String, Object> area = new HashMap<>();
        area.put("id" , 6);
        area.put("name" , "Paris");
        log.info("insert area {}" , area);
        int areaRow = areaMapper.insert(area);
        assertThat(areaRow).isEqualTo(1);

        Map<String, Object> dept = new HashMap<>();
        dept.put("id" , 6);
        dept.put("name" , "After-sales Service");
        dept.put("areaId" , 6);
        log.info("insert department {}" , dept);
        int deptRow = departmentMapper.insert(dept);
        assertThat(deptRow).isEqualTo(1);

        Map<String, Object> employee = new HashMap<>();
        employee.put("id" , 6);
        employee.put("name" , "Peng He");
        employee.put("departmentId" , 6);
        log.info("insert employee {}" , dept);
        int employeeRow = employeeMapper.insert(employee);
        assertThat(employeeRow).isEqualTo(1);

        assertThat(RoutingContext.getRoutedDataSourceName()).isEqualTo("write_0");

        String sql = "SELECT a.name AS area_name, d.name AS department_name, e.name AS employee_name\n" +
                "FROM area a\n" +
                "JOIN department d ON a.id = d.area_id\n" +
                "JOIN employee e ON d.id = e.department_id\n" +
                "WHERE a.id = ?";

        List<Map<String, Object>> results = jdbcTemplate.query(sql, new Object[]{6}, new ColumnMapRowMapper());
        assertThat(results)
                .extracting("AREA_NAME", "DEPARTMENT_NAME" , "EMPLOYEE_NAME")
                .containsExactlyInAnyOrder(
                        tuple("Paris", "After-sales Service", "Peng He")
                );

        assertThat(RoutingContext.getRoutedDataSourceName()).isEqualTo("write_0");
    }

    @Transactional(rollbackFor = Throwable.class , readOnly = true)
    @Commit
    @DirtiesContext
    @Test
    @Order(2)
    void testReadOnlyTxFunc() {

        log.info("testing read only Tx");
        assertThat(RoutingContext.inTx()).isEqualTo(true);
        assertThat(RoutingContext.getTxReadOnly()).isEqualTo(true);

        Map<String, Object> area = areaMapper.selectById(1L);
        assertThat(area).isNotNull()
                .extracting("ID" , "NAME")
                .containsExactlyInAnyOrder(1L , "East");

        Map<String, Object> dept = departmentMapper.selectById(1L);
        assertThat(dept).isNotNull()
                .extracting("ID" , "NAME" , "AREA_ID")
                .containsExactlyInAnyOrder(1L , "Research and Development" , 1);

        Map<String, Object> employee = employeeMapper.selectById(1L);
        assertThat(employee).isNotNull()
                .extracting("ID" , "NAME" , "DEPARTMENT_ID")
                .containsExactlyInAnyOrder(1L , "John Doe" , 1);

        String sql = "SELECT a.name AS area_name, d.name AS department_name, e.name AS employee_name , e.id as employee_id \n" +
                "FROM area a\n" +
                "JOIN department d ON a.id = d.area_id\n" +
                "JOIN employee e ON d.id = e.department_id\n" +
                "WHERE a.id = ?";

        List<Map<String, Object>> results = jdbcTemplate.query(sql, new Object[]{1}, new ColumnMapRowMapper());
        assertThat(results)
                .extracting("AREA_NAME", "DEPARTMENT_NAME" , "EMPLOYEE_NAME" , "EMPLOYEE_ID")
                .containsExactlyInAnyOrder(
                        tuple("East", "Research and Development", "John Doe" , 1L),
                        tuple("East", "Research and Development", "Jane Doe" , 2L)
                );

        assertThat(RoutingContext.getRoutedDataSourceName()).containsAnyOf("read_0" , "read_1");
    }

    @DirtiesContext
    @Test
    @Order(3)
    void testReadWriteNoTxFunc() {
        log.info("testing read write No Tx");
        assertThat(RoutingContext.inTx()).isEqualTo(false);

        Map<String, Object> area = new HashMap<>();
        area.put("id" , 6);
        area.put("name" , "Paris");
        log.info("insert area {}" , area);
        int areaRow = areaMapper.insert(area);
        assertThat(areaRow).isEqualTo(1);

        Map<String, Object> dept = new HashMap<>();
        dept.put("id" , 6);
        dept.put("name" , "After-sales Service");
        dept.put("areaId" , 6);
        log.info("insert department {}" , dept);
        int deptRow = departmentMapper.insert(dept);
        assertThat(deptRow).isEqualTo(1);

        Map<String, Object> employee = new HashMap<>();
        employee.put("id" , 6);
        employee.put("name" , "Peng He");
        employee.put("departmentId" , 6);
        log.info("insert employee {}" , dept);
        int employeeRow = employeeMapper.insert(employee);
        assertThat(employeeRow).isEqualTo(1);

        String sql = "SELECT a.name AS area_name, d.name AS department_name, e.name AS employee_name\n" +
                "FROM area a\n" +
                "JOIN department d ON a.id = d.area_id\n" +
                "JOIN employee e ON d.id = e.department_id\n" +
                "WHERE a.id = ?";

        RoutingContext.forceWrite();
        List<Map<String, Object>> results = jdbcTemplate.query(sql, new Object[]{6}, new ColumnMapRowMapper());
        assertThat(results)
                .extracting("AREA_NAME", "DEPARTMENT_NAME" , "EMPLOYEE_NAME")
                .containsExactlyInAnyOrder(
                        tuple("Paris", "After-sales Service", "Peng He")
                );

    }

    @DirtiesContext
    @Test
    @Order(4)
    void testReadOnlyNoTxFunc() {
        log.info("testing read only No Tx");
        assertThat(RoutingContext.inTx()).isEqualTo(false);

        Map<String, Object> area = areaMapper.selectById(1L);
        assertThat(area).isNotNull()
                .extracting("ID" , "NAME")
                .containsExactlyInAnyOrder(1L , "East");

        Map<String, Object> dept = departmentMapper.selectById(1L);
        assertThat(dept).isNotNull()
                .extracting("ID" , "NAME" , "AREA_ID")
                .containsExactlyInAnyOrder(1L , "Research and Development" , 1);

        Map<String, Object> employee = employeeMapper.selectById(1L);
        assertThat(employee).isNotNull()
                .extracting("ID" , "NAME" , "DEPARTMENT_ID")
                .containsExactlyInAnyOrder(1L , "John Doe" , 1);

        String sql = "SELECT a.name AS area_name, d.name AS department_name, e.name AS employee_name , e.id as employee_id \n" +
                "FROM area a\n" +
                "JOIN department d ON a.id = d.area_id\n" +
                "JOIN employee e ON d.id = e.department_id\n" +
                "WHERE a.id = ?";

        List<Map<String, Object>> results = jdbcTemplate.query(sql, new Object[]{1}, new ColumnMapRowMapper());
        assertThat(results)
                .extracting("AREA_NAME", "DEPARTMENT_NAME" , "EMPLOYEE_NAME" , "EMPLOYEE_ID")
                .containsExactlyInAnyOrder(
                        tuple("East", "Research and Development", "John Doe" , 1L),
                        tuple("East", "Research and Development", "Jane Doe" , 2L)
                );
    }

    @AfterEach
    @Override
    protected void clearData() throws Exception {
        if (RoutingContext.inTx()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCompletion(int status) {
                    try {
                        TransactionalTest.super.clearData();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        } else {
            super.clearData();
        }
    }
}
Spring 配置文件测试

针对 Spring 集成时配置文件测试,我们分别测试了 properties 格式和 yaml 格式. 通过 @TestPropertySource(locations = "classpath:application.yaml") 注解来指定当前单元测试类所使用的是那个配置文件.配置文件中我们定义了三个数据源 write_0 , read_0 , read_1 . 配置了那个数据源时写节点,哪些数据源是读节点. 通过 routing.rules.tables 配置了基于表的路由策略. 在基于表的路由策略中一张表可以配置多个访问数据源, 每个数据源可以配置支持访问的 SQL 语句类型, 例如 employee 表允许使用 read_0read_1 数据源访问当执行的 SQL 类型是 SELECT 类型时.

application.properties 配置文件


logging.level.root=debug

############################# mybatis configuration #############################
mybatis.configuration.use-actual-param-name=true

############################# routing configuration #############################
routing.dataSources.write_0.type=READ_WRITE
routing.dataSources.write_0.weight=99
routing.dataSources.write_0.dataSourceClass=com.zaxxer.hikari.HikariDataSource
#routing.dataSources.write_0.properties.driverClassName=org.h2.Driver
routing.dataSources.write_0.properties.jdbcUrl=jdbc:h2:mem:~/test1;FILE_LOCK=SOCKET;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;AUTO_RECONNECT=TRUE;IGNORECASE=TRUE;
routing.dataSources.write_0.properties.username=sa
routing.dataSources.write_0.properties.password=
routing.dataSources.write_0.properties.minIdle=5
routing.dataSources.write_0.properties.maxPoolSize=30
routing.dataSources.write_0.properties.connectionTimeout=30000
routing.dataSources.write_0.properties.isAutoCommit=false
routing.dataSources.write_0.properties.isReadOnly=false

routing.dataSources.read_0.type=READ
routing.dataSources.read_0.weight=6
routing.dataSources.read_0.dataSourceClass=com.zaxxer.hikari.HikariDataSource
#routing.dataSources.read_0.driverClassName=org.h2.Driver
routing.dataSources.read_0.properties.jdbcUrl=jdbc:h2:mem:~/test2;FILE_LOCK=SOCKET;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;AUTO_RECONNECT=TRUE;IGNORECASE=TRUE;
routing.dataSources.read_0.properties.username=sa
routing.dataSources.read_0.properties.password=
routing.dataSources.read_0.properties.minIdle=10
routing.dataSources.read_0.properties.maxPoolSize=30
routing.dataSources.read_0.properties.connectionTimeout=40000
routing.dataSources.read_0.properties.isAutoCommit=false
routing.dataSources.read_0.properties.isReadOnly=true

routing.dataSources.read_1.type=READ
routing.dataSources.read_1.weight=10
routing.dataSources.read_1.dataSourceClass=com.zaxxer.hikari.HikariDataSource
#routing.dataSources.read_1.properties.driverClassName=org.h2.Driver
routing.dataSources.read_1.properties.jdbcUrl=jdbc:h2:mem:~/test3;FILE_LOCK=SOCKET;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;AUTO_RECONNECT=TRUE;IGNORECASE=TRUE;
routing.dataSources.read_1.properties.username=sa
routing.dataSources.read_1.properties.password=
routing.dataSources.read_1.properties.minIdle=15
routing.dataSources.read_1.properties.maxPoolSize=30
routing.dataSources.read_1.properties.connectionTimeout=60000
routing.dataSources.read_1.properties.isAutoCommit=false
routing.dataSources.read_1.properties.isReadOnly=true

routing.writeDataSource=write_0
routing.readDataSources[0]=read_0
routing.readDataSources[1]=read_1

# table routing rule
routing.rules.tables.employee.write_0.sqlTypes[0]=INSERT
routing.rules.tables.employee.write_0.sqlTypes[1]=UPDATE
routing.rules.tables.employee.write_0.sqlTypes[2]=DELETE
routing.rules.tables.employee.write_0.sqlTypes[3]=OTHER

routing.rules.tables.employee.read_0.sqlTypes[0]=SELECT
routing.rules.tables.employee.read_1.sqlTypes[0]=SELECT

application.yaml 配置文件


logging:
  level:
    root: debug

############################# mybatis configuration #############################

mybatis:
  configuration:
    use-actual-param-name: true

############################# routing configuration #############################

routing:
  dataSources:
    write_0:
      type: READ_WRITE
      weight: 99
      dataSourceClass: com.zaxxer.hikari.HikariDataSource
      properties:
        jdbcUrl: jdbc:h2:mem:~/test1;FILE_LOCK=SOCKET;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;AUTO_RECONNECT=TRUE;IGNORECASE=TRUE;
        username: xa
        password:
        minIdle: 5
        maxPoolSize: 30
        connectionTimeout: 30000
        isAutoCommit: false
        isReadOnly: false

    read_0:
      type: READ
      weight: 6
      dataSourceClass: com.zaxxer.hikari.HikariDataSource
      properties:
        jdbcUrl: jdbc:h2:mem:~/test2;FILE_LOCK=SOCKET;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;AUTO_RECONNECT=TRUE;IGNORECASE=TRUE;
        username: xa
        password:
        minIdle: 10
        maxPoolSize: 30
        connectionTimeout: 40000
        isAutoCommit: false
        isReadOnly: true

    read_1:
      type: READ
      weight: 10
      dataSourceClass: com.zaxxer.hikari.HikariDataSource
      properties:
        jdbcUrl: jdbc:h2:mem:~/test3;FILE_LOCK=SOCKET;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;AUTO_RECONNECT=TRUE;IGNORECASE=TRUE;
        username: xa
        password:
        minIdle: 15
        maxPoolSize: 30
        connectionTimeout: 60000
        isAutoCommit: false
        isReadOnly: true

  writeDataSource: write_0
  readDataSources:
    - read_0
    - read_1

  rules:
    tables:
      employee:
        write_0:
          sqlTypes:
            - INSERT
            - UPDATE
            - DELETE
            - OTHER
        read_0:
          sqlTypes:
            - SELECT
        read_1:
          sqlTypes:
            - SELECT

YamlConfigFormatTest.java

/**
     * testing yaml config file
     * @author Peng He
     * @since 1.0
     */

    @TestPropertySource(locations = "classpath:application.yaml")
    @Slf4j
    class YamlConfigFormatTest extends SpringBootIntegrationTest {

        @Autowired
        RoutingDataSourceProperties properties;

        @Test
        void testYamlFileDataSourcesConfig() {

            log.info("testing DataSourceConfiguration using spring boot yaml config file");

            assertThat(properties).isNotNull();
            assertThat(properties.getDataSources()).isNotEmpty();
            assertThat(properties.getDataSources()).containsOnlyKeys("write_0" , "read_0" , "read_1");

            assertWrite0();
            assertRead0();
            assertRead1();
        }

        @Test
        void testYamlFileTableRoutingRuleConfig() {

            log.info("testing Table Routing Rule using spring boot yaml config file");

            assertThat(properties.getRules()).isNotNull();
            Map<String, Map<String, SqlTypeConfiguration>> tables = properties.getRules().getTables();
            assertThat(tables).isNotEmpty().containsOnlyKeys("employee");

            for (Map.Entry<String, Map<String, SqlTypeConfiguration>> entry : tables.entrySet()) {
                Map<String, SqlTypeConfiguration> datasourceMap = entry.getValue();
                assertThat(datasourceMap).isNotEmpty();
                assertThat(datasourceMap).containsOnlyKeys("write_0" , "read_0" , "read_1");
                SqlTypeConfiguration write0 = datasourceMap.get("write_0");
                assertThat(write0).isNotNull();
                assertThat(write0.getAllowAllSqlTypes()).isNull();
                assertThat(write0.getSqlTypes()).containsOnly(SqlType.INSERT , SqlType.UPDATE , SqlType.DELETE , SqlType.OTHER);

                SqlTypeConfiguration read0 = datasourceMap.get("read_0");
                assertThat(read0).isNotNull();
                assertThat(read0.getAllowAllSqlTypes()).isNull();
                assertThat(read0.getSqlTypes()).containsOnly(SqlType.SELECT);

                SqlTypeConfiguration read1 = datasourceMap.get("read_1");
                assertThat(read1).isNotNull();
                assertThat(read1.getAllowAllSqlTypes()).isNull();
                assertThat(read1.getSqlTypes()).containsOnly(SqlType.SELECT);
            }
        }

        private void assertWrite0() {
            DataSourceConfiguration write0Configuration = properties.getDataSources().get("write_0");
            assertThat(write0Configuration).isNotNull();
            assertThat(write0Configuration.getType()).isEqualTo(RoutingTargetType.READ_WRITE);
            assertThat(write0Configuration.getDataSourceClass()).isEqualTo("com.zaxxer.hikari.HikariDataSource");
            assertThat(write0Configuration.getWeight()).isEqualTo(99);

            Map<String, Object> write0Props = write0Configuration.getProperties();
            assertThat(write0Props).isNotNull();
            assertThat(write0Props).extracting("jdbcUrl").isEqualTo("jdbc:h2:mem:~/test1;FILE_LOCK=SOCKET;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;AUTO_RECONNECT=TRUE;IGNORECASE=TRUE;");
            assertThat(write0Props).extracting("username").isEqualTo("sa");
            assertThat(write0Props).extracting("password").isEqualTo("");
            assertThat(write0Props).extracting("minIdle").isEqualTo("5");
            assertThat(write0Props).extracting("maxPoolSize").isEqualTo("30");
            assertThat(write0Props).extracting("connectionTimeout").isEqualTo("30000");
            assertThat(write0Props).extracting("isAutoCommit").isEqualTo("false");
            assertThat(write0Props).extracting("isReadOnly").isEqualTo("false");

            log.info("write_0 DataSourceConfiguration testing pass {} " , write0Configuration);
        }

        private void assertRead0() {
            DataSourceConfiguration read0Configuration = properties.getDataSources().get("read_0");
            assertThat(read0Configuration).isNotNull();
            assertThat(read0Configuration.getType()).isEqualTo(RoutingTargetType.READ);
            assertThat(read0Configuration.getDataSourceClass()).isEqualTo("com.zaxxer.hikari.HikariDataSource");
            assertThat(read0Configuration.getWeight()).isEqualTo(6);

            Map<String, Object> read0Props = read0Configuration.getProperties();
            assertThat(read0Props).isNotNull();
            assertThat(read0Props).extracting("jdbcUrl").isEqualTo("jdbc:h2:mem:~/test2;FILE_LOCK=SOCKET;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;AUTO_RECONNECT=TRUE;IGNORECASE=TRUE;");
            assertThat(read0Props).extracting("username").isEqualTo("sa");
            assertThat(read0Props).extracting("password").isEqualTo("");
            assertThat(read0Props).extracting("minIdle").isEqualTo("10");
            assertThat(read0Props).extracting("maxPoolSize").isEqualTo("30");
            assertThat(read0Props).extracting("connectionTimeout").isEqualTo("40000");
            assertThat(read0Props).extracting("isAutoCommit").isEqualTo("false");
            assertThat(read0Props).extracting("isReadOnly").isEqualTo("true");

            log.info("read_0 DataSourceConfiguration testing pass {} " , read0Configuration);
        }

        private void assertRead1() {
            DataSourceConfiguration read1Configuration = properties.getDataSources().get("read_1");
            assertThat(read1Configuration).isNotNull();
            assertThat(read1Configuration.getType()).isEqualTo(RoutingTargetType.READ);
            assertThat(read1Configuration.getDataSourceClass()).isEqualTo("com.zaxxer.hikari.HikariDataSource");
            assertThat(read1Configuration.getWeight()).isEqualTo(10);

            Map<String, Object> read1Props = read1Configuration.getProperties();
            assertThat(read1Props).isNotNull();
            assertThat(read1Props).extracting("jdbcUrl").isEqualTo("jdbc:h2:mem:~/test3;FILE_LOCK=SOCKET;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=TRUE;AUTO_RECONNECT=TRUE;IGNORECASE=TRUE;");
            assertThat(read1Props).extracting("username").isEqualTo("sa");
            assertThat(read1Props).extracting("password").isEqualTo("");
            assertThat(read1Props).extracting("minIdle").isEqualTo("15");
            assertThat(read1Props).extracting("maxPoolSize").isEqualTo("30");
            assertThat(read1Props).extracting("connectionTimeout").isEqualTo("60000");
            assertThat(read1Props).extracting("isAutoCommit").isEqualTo("false");
            assertThat(read1Props).extracting("isReadOnly").isEqualTo("true");

            log.info("read_1 DataSourceConfiguration testing pass {} " , read1Configuration);
        }

    }

如果你有任何的建议欢迎留言,我们愿意倾听任何建议,同时持续的改进。Anyway we are humble, open, and stay foolish, hoping you can have some fun.


DevX 会持续分享有趣的技术和见闻,如果你觉得本文对你有帮助希望你可以分享给更多的朋友看到。该文章会同步在微信公众号 【DevXJava】, 方便在微信客户端阅读。

DevX 不止于技术