单元测试最好不依赖实际的数据库,在任何环境都可以跑。本文使用h2内存数据库,liquibase初始化测试数据,embedded-redis内存redis等来摆脱环境依赖。

1.技术框架和环境

springboot,idea,maven,liquibase,h2,spock,embedded-redis,mybatis-plus等。

2.工程结构

在这里插入图片描述

3. pom.xml配置

 <properties>
 	<liquibase.version>3.8.9</liquibase.version>
    <h2.version>1.4.197</h2.version>
    <groovy.version>2.5.14</groovy.version>
    <spock.version>1.3-groovy-2.5</spock.version>
    <cglib-nodep.version>2.2</cglib-nodep.version>
    <junit-platform-launcher>1.7.1</junit-platform-launcher>
    <embedded-redis>0.7.3</embedded-redis>
 </properties>

<dependencies>
	 <dependency>
       <groupId>org.liquibase</groupId>
       <artifactId>liquibase-core</artifactId>
       <version>${liquibase.version}</version>
     </dependency>
	<dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>${h2.version}</version>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy</artifactId>
        <version>${groovy.version}</version>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>org.spockframework</groupId>
        <artifactId>spock-core</artifactId>
        <version>${spock.version}</version>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>org.spockframework</groupId>
        <artifactId>spock-spring</artifactId>
        <version>${spock.version}</version>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>${cglib-nodep.version}</version>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>it.ozimov</groupId>
        <artifactId>embedded-redis</artifactId>
        <version>${embedded-redis}</version>
        <scope>test</scope>
      </dependency>
</dependencies>

4. application-test.yml配置

server:
  port: 8000
  servlet:
    context-path: /code_proxy
spring:
  profiles:
    active: test
  application:
    name: freedom-code
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    # MYSQL 5 驱动:com.mysql.jdbc.Driver,MYSQL 6+ 驱动:com.mysql.cj.jdbc.Driver
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:mybatisplus;MODE=Mysql;DB_CLOSE_DELAY=1;TRACE_LEVEL_SYSTEM_OUT=2;
    username: root
    password: root
    druid:
      # 初始化大小,最小,最大
      initial-size: 5
      min-idle: 5
      max-active: 100
      # 配置获取连接等待超时的时间
      max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存时间
      min-evictable-idle-time-millis: 300000
      validation-query: select VERSION()
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false

  liquibase:
    change-log: 'classpath:/db/changelog/db.mysql-mater.xml'
    database-change-log-lock-table: databasechangeloglock
    database-change-log-table: databasechangelog
    drop-first: false
    enabled: true
    test-rollback-on-update: false

  h2:
    console:
      enabled: true
      path: /h2-console

  redis:
    database: 2
    host: localhost
    lettuce:
      pool:
        max-active: 16
        max-idle: 8
        max-wait: 4000
        min-idle: 6
    port: 6379
    timeout: 4000

# mybatisplus的配置
mybatis-plus:
  configuration:
    # 是否开启自动驼峰命名规则
    map-underscore-to-camel-case: true
    # 对所有的 resultMap 都进行自动映射
    auto-mapping-behavior: full
    # #控制台打印完整带参数SQL语句
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/**/*.xml

# ===== 自定义swagger配置 ===== #
swagger:
  enable: true
  application-name: ${spring.application.name}
  application-version: 1.0
  application-description: springfox swagger 3.0 demo
  try-host: http://localhost:${server.port}

test:
  conditional:
    enabled: true

5. liquibase配置

spring:
  liquibase:
    change-log: 'classpath:/db/changelog/db.mysql-mater.xml'
    database-change-log-lock-table: databasechangeloglock
    database-change-log-table: databasechangelog
    drop-first: false
    enabled: true
    test-rollback-on-update: false

在这里插入图片描述
在这里插入图片描述

6. 关键代码配置

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootTest(classes = FreedomApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@Stepwise
public @interface CodeBootTest {

}

@ConditionalOnProperty(prefix = "test.conditional", name = "enabled", havingValue = "true")
@Configuration
@EnableRedisRepositories
public class TestRedisConfig {

  @Bean
  public LettuceConnectionFactory redisConnectionFactory(RedisProperties redisProperties) {
    return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort());
  }

  @Bean
  public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(connectionFactory);
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new StringRedisSerializer());
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
    return template;
  }

}

@ConditionalOnProperty(prefix = "test.conditional", name = "enabled", havingValue = "true")
@Configuration
public class TestRedisServiceConfig {

  private RedisServer redisServer;

  public TestRedisServiceConfig(RedisProperties redisProperties) {
    // this.redisServer = new RedisServer(redisProperties.getPort());
    this.redisServer = RedisServer.builder().port(redisProperties.getPort()).setting("maxmemory 128M").build();
  }

  @PostConstruct
  public void postConstruct() {
    redisServer.start();
  }

  @PreDestroy
  public void preDestroy() {
    redisServer.stop();
  }
}

测试案例

@CodeBootTest
class UserServiceImplSpec extends Specification {
    @Autowired
    private UserService userService;
    @Autowired
    private RedisTemplate redisTemplate;

    def "getById"() {
        //sleep(165453646464546)
        when: 'start'
        UserDO userDO = userService.getById(id)

        then: 'assert'
        userDO != null

        and: 'set value'
        def value = userDO.id

        expect: 'expect'
        value == expectedResult

        where: 'sucess'
        id                   || expectedResult
        1345368146133819649L || 1345368146133819649L
    }
}

7. 测试报告

执行mvn test命令获得以下报告信息;
在这里插入图片描述
在这里插入图片描述

8. 代码地址

https://github.com/tobebetter9527/freedom-framework/tree/v1.0.0alpha

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐