• 单元测试教程
    • 示例项目
    • 技术
    • pom依赖和插件
    • 目录结构
    • 编写测试groovy脚本
    • 其他
    • spock其他用法
    • 参考

    单元测试教程


    示例项目

    asgard-service

    技术

    框架:spock

    语言:groovy

    pom依赖和插件

    1. <!-- Test Dependencies -->
    2. <dependencies>
    3. <dependency>
    4. <groupId>org.springframework.boot</groupId>
    5. <artifactId>spring-boot-starter-test</artifactId>
    6. <scope>test</scope>
    7. </dependency>
    8. <dependency>
    9. <groupId>io.choerodon</groupId>
    10. <artifactId>choerodon-liquibase</artifactId>
    11. <version>${choerodon.starters.version}</version>
    12. <scope>test</scope>
    13. </dependency>
    14. <dependency>
    15. <groupId>com.h2database</groupId>
    16. <artifactId>h2</artifactId>
    17. <version>1.4.197</version>
    18. <scope>test</scope>
    19. </dependency>
    20. <dependency>
    21. <groupId>org.spockframework</groupId>
    22. <artifactId>spock-core</artifactId>
    23. <version>1.1-groovy-2.4-rc-2</version>
    24. <scope>test</scope>
    25. </dependency>
    26. <dependency>
    27. <groupId>org.spockframework</groupId>
    28. <artifactId>spock-spring</artifactId>
    29. <version>1.1-groovy-2.4-rc-3</version>
    30. <scope>test</scope>
    31. </dependency>
    32. <!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
    33. <dependency>
    34. <groupId>cglib</groupId>
    35. <artifactId>cglib-nodep</artifactId>
    36. <version>2.2</version>
    37. <scope>test</scope>
    38. </dependency>
    39. </dependencies>
    40. <build>
    41. <finalName>app</finalName>
    42. <plugins>
    43. <plugin>
    44. <groupId>org.codehaus.gmavenplus</groupId>
    45. <artifactId>gmavenplus-plugin</artifactId>
    46. <version>1.5</version>
    47. <executions>
    48. <execution>
    49. <goals>
    50. <goal>addTestSources</goal>
    51. <goal>testCompile</goal>
    52. </goals>
    53. </execution>
    54. </executions>
    55. </plugin>
    56. <plugin>
    57. <groupId>org.apache.maven.plugins</groupId>
    58. <artifactId>maven-compiler-plugin</artifactId>
    59. <version>3.3</version>
    60. <configuration>
    61. <source>1.8</source>
    62. <target>1.8</target>
    63. </configuration>
    64. </plugin>
    65. <plugin>
    66. <groupId>org.apache.maven.plugins</groupId>
    67. <artifactId>maven-surefire-plugin</artifactId>
    68. <version>2.18.1</version>
    69. <configuration>
    70. <includes>
    71. <include>**/*Test.java</include>
    72. <include>**/*Spec.java</include>
    73. </includes>
    74. </configuration>
    75. </plugin>
    76. </plugins>
    77. </build>

    目录结构

    单元测试教程  - 图1

    IntegrationTestConfiguration.groovy

    1. @TestConfiguration
    2. @Import(LiquibaseConfig)
    3. class IntegrationTestConfiguration {
    4. private final detachedMockFactory = new DetachedMockFactory()
    5. @Value('${choerodon.oauth.jwt.key:choerodon}')
    6. String key
    7. @Autowired
    8. TestRestTemplate testRestTemplate
    9. @Autowired
    10. LiquibaseExecutor liquibaseExecutor
    11. final ObjectMapper objectMapper = new ObjectMapper()
    12. //Mock KafkaTemplate, 以防spring-for-kafka影响报错
    13. @Bean
    14. KafkaTemplate kafkaTemplate() {
    15. detachedMockFactory.Mock(KafkaTemplate)
    16. }
    17. @PostConstruct
    18. void init() {
    19. //通过liquibase初始化h2数据库
    20. liquibaseExecutor.execute()
    21. //给TestRestTemplate的请求头部添加JWT
    22. setTestRestTemplateJWT()
    23. }
    24. private void setTestRestTemplateJWT() {
    25. testRestTemplate.getRestTemplate().setRequestFactory(new HttpComponentsClientHttpRequestFactory())
    26. testRestTemplate.getRestTemplate().setInterceptors([new ClientHttpRequestInterceptor() {
    27. @Override
    28. ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
    29. httpRequest.getHeaders()
    30. .add('JWT_Token', createJWT(key, objectMapper))
    31. return clientHttpRequestExecution.execute(httpRequest, bytes)
    32. }
    33. }])
    34. }
    35. static String createJWT(final String key, final ObjectMapper objectMapper) {
    36. Signer signer = new MacSigner(key)
    37. CustomUserDetails defaultUserDetails = new CustomUserDetails('default', 'unknown', Collections.emptyList())
    38. defaultUserDetails.setUserId(0L)
    39. defaultUserDetails.setOrganizationId(0L)
    40. defaultUserDetails.setLanguage('zh_CN')
    41. defaultUserDetails.setTimeZone('CCT')
    42. String jwtToken = null
    43. try {
    44. jwtToken = 'Bearer ' + JwtHelper.encode(objectMapper.writeValueAsString(defaultUserDetails), signer).getEncoded()
    45. } catch (IOException e) {
    46. e.printStackTrace()
    47. }
    48. return jwtToken
    49. }
    50. }

    application-test.yml

    1. spring:
    2. cloud:
    3. bus:
    4. enabled: false # 关闭bus,否则kafka报错
    5. sleuth:
    6. stream:
    7. enabled: false # 关闭zipkin,否则kafka报错
    8. datasource: # 使用内存数据库h2
    9. password: sa
    10. url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=Mysql;TRACE_LEVEL_SYSTEM_OUT=2;
    11. username: sa
    12. autoconfigure: # 关闭LiquibaseAutoConfiguration和KafkaAutoConfiguration的自动化配置
    13. exclude: org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration
    14. hystrix:
    15. stream:
    16. queue:
    17. enabled: false # 关闭hystrix stream,否则kafka报错
    18. data:
    19. dir: src/main/resources
    20. eureka:
    21. client:
    22. enabled: false # 关闭eureka

    编写测试groovy脚本

    添加注解@SpringBootTest(webEnvironment = RANDOM_PORT)和@Import(IntegrationTestConfiguration)。继承Specification且类名为Spec后缀。注入所需要测试的类对象通过given(前提条件),when(触发条件),then(期望结果)。

    1. @SpringBootTest(webEnvironment = RANDOM_PORT)
    2. @Import(IntegrationTestConfiguration)
    3. class SagaTaskMapperSpec extends Specification {
    4. @Autowired
    5. SagaTaskMapper sagaTaskMapper
    6. def 'insert'() {
    7. given: '创建一个bean'
    8. SagaTask sagaTask = new SagaTask()
    9. def testCode = 'test_code'
    10. def testSagaCode = 'test_saga_code'
    11. sagaTask.setCode(testCode)
    12. sagaTask.setSagaCode(testSagaCode)
    13. sagaTask.setSeq(1)
    14. sagaTask.setIsEnabled(true)
    15. sagaTask.setMaxRetryCount(1)
    16. when: '插入数据库'
    17. sagaTaskMapper.insert(sagaTask)
    18. then: '返回ID'
    19. sagaTask.getId() != null
    20. when: '根据ID在数据库查询'
    21. def data = sagaTaskMapper.selectByPrimaryKey(sagaTask.getId())
    22. then: '对比数据'
    23. data.getCode() == testCode
    24. data.getSagaCode() == testSagaCode
    25. data.getSeq() == 1
    26. data.getIsEnabled()
    27. data.getMaxRetryCount() == 1
    28. }
    29. }

    其他

    如果在idea不能直接编译运行测试groovy文件,可能是因idea没有识别test/groovy目录。

    单元测试教程  - 图2

    spock其他用法

    • @Shared:多个测试方法中共享数据

    • @Stepwise: 当测试方法间存在依赖关系时,标明测试方法将严格按照其在源代码中声明的顺序执行

    • setupSpec(): 设置每个测试类的环境

    • setup(): 设置每个测试方法的环境,每个测试方法执行一次

    • cleanup(): 清理每个测试方法的环境,每个测试方法执行一次

    • cleanupSepc(): 清理每个测试类的环境

    • @Ignore: 忽略测试方法

    • @IgnoreRest:只测试这个方法,而忽略所有其他方法

    • @Timeout: 设置测试方法的超时时间,默认单位为秒

    参考

    https://blog.csdn.net/u012129558/article/details/78675421

    http://blog.codepipes.com/testing/spock-vs-junit.html