- 单元测试教程
- 示例项目
- 技术
- pom依赖和插件
- 目录结构
- 编写测试groovy脚本
- 其他
- spock其他用法
- 参考
单元测试教程
示例项目
asgard-service
技术
框架:spock
语言:groovy
pom依赖和插件
<!-- Test Dependencies --><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.choerodon</groupId><artifactId>choerodon-liquibase</artifactId><version>${choerodon.starters.version}</version><scope>test</scope></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><version>1.4.197</version><scope>test</scope></dependency><dependency><groupId>org.spockframework</groupId><artifactId>spock-core</artifactId><version>1.1-groovy-2.4-rc-2</version><scope>test</scope></dependency><dependency><groupId>org.spockframework</groupId><artifactId>spock-spring</artifactId><version>1.1-groovy-2.4-rc-3</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/cglib/cglib-nodep --><dependency><groupId>cglib</groupId><artifactId>cglib-nodep</artifactId><version>2.2</version><scope>test</scope></dependency></dependencies><build><finalName>app</finalName><plugins><plugin><groupId>org.codehaus.gmavenplus</groupId><artifactId>gmavenplus-plugin</artifactId><version>1.5</version><executions><execution><goals><goal>addTestSources</goal><goal>testCompile</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.3</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.18.1</version><configuration><includes><include>**/*Test.java</include><include>**/*Spec.java</include></includes></configuration></plugin></plugins></build>
目录结构

IntegrationTestConfiguration.groovy
@TestConfiguration@Import(LiquibaseConfig)class IntegrationTestConfiguration {private final detachedMockFactory = new DetachedMockFactory()@Value('${choerodon.oauth.jwt.key:choerodon}')String key@AutowiredTestRestTemplate testRestTemplate@AutowiredLiquibaseExecutor liquibaseExecutorfinal ObjectMapper objectMapper = new ObjectMapper()//Mock KafkaTemplate, 以防spring-for-kafka影响报错@BeanKafkaTemplate kafkaTemplate() {detachedMockFactory.Mock(KafkaTemplate)}@PostConstructvoid init() {//通过liquibase初始化h2数据库liquibaseExecutor.execute()//给TestRestTemplate的请求头部添加JWTsetTestRestTemplateJWT()}private void setTestRestTemplateJWT() {testRestTemplate.getRestTemplate().setRequestFactory(new HttpComponentsClientHttpRequestFactory())testRestTemplate.getRestTemplate().setInterceptors([new ClientHttpRequestInterceptor() {@OverrideClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {httpRequest.getHeaders().add('JWT_Token', createJWT(key, objectMapper))return clientHttpRequestExecution.execute(httpRequest, bytes)}}])}static String createJWT(final String key, final ObjectMapper objectMapper) {Signer signer = new MacSigner(key)CustomUserDetails defaultUserDetails = new CustomUserDetails('default', 'unknown', Collections.emptyList())defaultUserDetails.setUserId(0L)defaultUserDetails.setOrganizationId(0L)defaultUserDetails.setLanguage('zh_CN')defaultUserDetails.setTimeZone('CCT')String jwtToken = nulltry {jwtToken = 'Bearer ' + JwtHelper.encode(objectMapper.writeValueAsString(defaultUserDetails), signer).getEncoded()} catch (IOException e) {e.printStackTrace()}return jwtToken}}
application-test.yml
spring:cloud:bus:enabled: false # 关闭bus,否则kafka报错sleuth:stream:enabled: false # 关闭zipkin,否则kafka报错datasource: # 使用内存数据库h2password: saurl: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=Mysql;TRACE_LEVEL_SYSTEM_OUT=2;username: saautoconfigure: # 关闭LiquibaseAutoConfiguration和KafkaAutoConfiguration的自动化配置exclude: org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,org.springframework.boot.autoconfigure.kafka.KafkaAutoConfigurationhystrix:stream:queue:enabled: false # 关闭hystrix stream,否则kafka报错data:dir: src/main/resourceseureka:client:enabled: false # 关闭eureka
编写测试groovy脚本
添加注解@SpringBootTest(webEnvironment = RANDOM_PORT)和@Import(IntegrationTestConfiguration)。继承Specification且类名为Spec后缀。注入所需要测试的类对象通过given(前提条件),when(触发条件),then(期望结果)。
@SpringBootTest(webEnvironment = RANDOM_PORT)@Import(IntegrationTestConfiguration)class SagaTaskMapperSpec extends Specification {@AutowiredSagaTaskMapper sagaTaskMapperdef 'insert'() {given: '创建一个bean'SagaTask sagaTask = new SagaTask()def testCode = 'test_code'def testSagaCode = 'test_saga_code'sagaTask.setCode(testCode)sagaTask.setSagaCode(testSagaCode)sagaTask.setSeq(1)sagaTask.setIsEnabled(true)sagaTask.setMaxRetryCount(1)when: '插入数据库'sagaTaskMapper.insert(sagaTask)then: '返回ID'sagaTask.getId() != nullwhen: '根据ID在数据库查询'def data = sagaTaskMapper.selectByPrimaryKey(sagaTask.getId())then: '对比数据'data.getCode() == testCodedata.getSagaCode() == testSagaCodedata.getSeq() == 1data.getIsEnabled()data.getMaxRetryCount() == 1}}
其他
如果在idea不能直接编译运行测试groovy文件,可能是因idea没有识别test/groovy目录。

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
