单元测试JunitTest加@Transactional事务自动回滚

问题

在测试事务传播行为的时候,使用单位测试加了@Transactional,一开始是正常,后面出现了异常,即使没有报错的情况下,事务也会自动回滚

代码

@RunWith(SpringRunner.class)
@SpringBootTest
public class TranTest {

	@Autowired
	private KsAService ksAService;

	@Autowired
	private KsBService ksBService;

	@Test
	@Transactional
	public void test() {
		KsA ksA = new KsA();
		ksA.setName("林");
		ksAService.insert(ksA);
		KsB ksB = new KsB();
		ksB.setAge(10);
		ksBService.insert(ksB);
	}
}

异常

2022-10-20 11:27:31.818 [TID: N/A] [main] INFO  o.s.t.c.t.TransactionContext -Began transaction (1) for test context [DefaultTestContext@c430e6c testClass = TranTest, testInstance = com.forlan.kaoshi.TranTest@ce0c2b3, testMethod = test@TranTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@312aa7c testClass = TranTest, locations = '{}', classes = '{class com.forlan.kaoshi.KaoshiApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@418e7838, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@3e2e18f2, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@48ae9b55, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@72f926e6, org.spockframework.spring.mock.SpockContextCustomizer@0], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]]; transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@1e1b512e]; rollback [true]
2022-10-20 11:27:32.296 [TID: N/A] [main] INFO  c.m.interceptor.TenancyInterceptor -TenancyInterceptor插入日志信息->KsAMapper.insert->【系统调用】sql : insert into ks_a(name) values ('林')
2022-10-20 11:27:32.298 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsAMapper.insert -==>  Preparing: insert into ks_a(name) values (?) 
2022-10-20 11:27:32.316 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsAMapper.insert -==> Parameters: 林(String)
2022-10-20 11:27:32.365 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsAMapper.insert -<==    Updates: 1
2022-10-20 11:27:32.377 [TID: N/A] [main] INFO  c.m.interceptor.TenancyInterceptor -TenancyInterceptor插入日志信息->KsBDao.insert->【系统调用】sql : insert into ks_b(age) values (10)
2022-10-20 11:27:32.377 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsBDao.insert -==>  Preparing: insert into ks_b(age) values (?) 
2022-10-20 11:27:32.377 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsBDao.insert -==> Parameters: 10(Integer)
2022-10-20 11:27:32.421 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsBDao.insert -<==    Updates: 1
2022-10-20 11:27:32.470 [TID: N/A] [main] INFO  o.s.t.c.t.TransactionContext -Rolled back transaction for test: [DefaultTestContext@c430e6c testClass = TranTest, testInstance = com.forlan.kaoshi.TranTest@ce0c2b3, testMethod = test@TranTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@312aa7c testClass = TranTest, locations = '{}', classes = '{class com.forlan.kaoshi.KaoshiApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@418e7838, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@3e2e18f2, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@48ae9b55, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@72f926e6, org.spockframework.spring.mock.SpockContextCustomizer@0], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]]

Rolled back transaction for test,数据都回滚了
在这里插入图片描述
在这里插入图片描述

解决

1、方法加注解@Rollback(false)

该注解是直接关闭了自动回滚,异常情况下,数据也不会回滚

	@Test
	@Transactional
	@Rollback(false)
	public void test(){
		......
	}
2022-10-20 11:24:36.132 [TID: N/A] [main] INFO  o.s.t.c.t.TransactionContext -Began transaction (1) for test context [DefaultTestContext@66982506 testClass = TranTest, testInstance = com.forlan.kaoshi.TranTest@5efa2b92, testMethod = test@TranTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@70cf32e3 testClass = TranTest, locations = '{}', classes = '{class com.forlan.kaoshi.KaoshiApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@3e2e18f2, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@3e92efc3, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@30ee2816, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@27ce24aa, org.spockframework.spring.mock.SpockContextCustomizer@0], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]]; transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@1838e02]; rollback [false]
2022-10-20 11:24:38.073 [TID: N/A] [main] INFO  c.m.interceptor.TenancyInterceptor -TenancyInterceptor插入日志信息->KsAMapper.insert->【系统调用】sql : insert into ks_a(name) values ('林')
2022-10-20 11:24:38.077 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsAMapper.insert -==>  Preparing: insert into ks_a(name) values (?) 
2022-10-20 11:24:38.113 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsAMapper.insert -==> Parameters: 林(String)
2022-10-20 11:24:38.134 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsAMapper.insert -<==    Updates: 1
2022-10-20 11:24:38.249 [TID: N/A] [main] INFO  c.m.interceptor.TenancyInterceptor -TenancyInterceptor插入日志信息->KsBDao.insert->【系统调用】sql : insert into ks_b(age) values (10)
2022-10-20 11:24:38.249 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsBDao.insert -==>  Preparing: insert into ks_b(age) values (?) 
2022-10-20 11:24:38.249 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsBDao.insert -==> Parameters: 10(Integer)
2022-10-20 11:24:38.267 [TID: N/A] [main] DEBUG c.m.k.forlan.mapper.KsBDao.insert -<==    Updates: 1
2022-10-20 11:24:38.323 [TID: N/A] [main] INFO  o.s.t.c.t.TransactionContext -Committed transaction for test: [DefaultTestContext@66982506 testClass = TranTest, testInstance = com.forlan.kaoshi.TranTest@5efa2b92, testMethod = test@TranTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@70cf32e3 testClass = TranTest, locations = '{}', classes = '{class com.forlan.kaoshi.KaoshiApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@3e2e18f2, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@3e92efc3, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@30ee2816, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@27ce24aa, org.spockframework.spring.mock.SpockContextCustomizer@0], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]]

数据成功入库
在这里插入图片描述
在这里插入图片描述

2、使用http访问

通过TestRestTemplate 访问controller方法

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class TranTest {

	@Test
	public void add() throws Exception {
		TestRestTemplate restTemplate = new TestRestTemplate();
		restTemplate.postForLocation("http://localhost:8100/kaoshi/forlan/test/add", KsA.class);
	}

}

@RestController
@RequestMapping("/forlan/test")
public class TestController {

	@Autowired
	private KsAService ksAService;
	@Autowired
	private KsBService ksBService;

	@PostMapping("/add")
	@Transactional
	public void add() {
		KsA ksA = new KsA();
		ksA.setName("林");
		ksAService.insert(ksA);
		KsB ksB = new KsB();
		ksB.setAge(10);
		ksBService.insert(ksB);
	}
}
2022-10-27 16:10:46.053 [TID: N/A] [http-nio-8100-exec-1] INFO  o.a.c.c.C.[.[localhost].[/forlan] -Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-10-27 16:10:46.053 [TID: N/A] [http-nio-8100-exec-1] INFO  o.s.web.servlet.DispatcherServlet -Initializing Servlet 'dispatcherServlet'
2022-10-27 16:10:46.158 [TID: N/A] [http-nio-8100-exec-1] INFO  o.s.web.servlet.DispatcherServlet -Completed initialization in 105 ms
2022-10-27 16:10:46.211 [TID: N/A] [http-nio-8100-exec-1] INFO  c.m.k.common.config.ControllerAspect -request(2666eff88910474a8f0a921c5da76454)【请求方法TestController.add1(),参数:[]】 end 
2022-10-27 16:10:46.227 [TID: N/A] [http-nio-8100-exec-1] INFO  com.zaxxer.hikari.HikariDataSource -DatebookHikariCP - Starting...
2022-10-27 16:10:46.519 [TID: N/A] [http-nio-8100-exec-1] INFO  com.zaxxer.hikari.HikariDataSource -DatebookHikariCP - Start completed.
2022-10-27 16:10:46.577 [TID: N/A] [http-nio-8100-exec-1] INFO  c.m.interceptor.TenancyInterceptor -TenancyInterceptor插入日志信息->KsAMapper.insert->租户【系统调用】sql : insert into ks_a(name) values ('林')
2022-10-27 16:10:46.580 [TID: N/A] [http-nio-8100-exec-1] DEBUG c.m.k.forlan.mapper.KsAMapper.insert -==>  Preparing: insert into ks_a(name) values (?) 
2022-10-27 16:10:46.601 [TID: N/A] [http-nio-8100-exec-1] DEBUG c.m.k.forlan.mapper.KsAMapper.insert -==> Parameters: 林(String)
2022-10-27 16:10:46.615 [TID: N/A] [http-nio-8100-exec-1] DEBUG c.m.k.forlan.mapper.KsAMapper.insert -<==    Updates: 1
2022-10-27 16:10:46.627 [TID: N/A] [http-nio-8100-exec-1] INFO  c.m.interceptor.TenancyInterceptor -TenancyInterceptor插入日志信息->KsBDao.insert->租户【系统调用】sql : insert into ks_b(age) values (10)
2022-10-27 16:10:46.627 [TID: N/A] [http-nio-8100-exec-1] DEBUG c.m.k.forlan.mapper.KsBDao.insert -==>  Preparing: insert into ks_b(age) values (?) 
2022-10-27 16:10:46.628 [TID: N/A] [http-nio-8100-exec-1] DEBUG c.m.k.forlan.mapper.KsBDao.insert -==> Parameters: 10(Integer)
2022-10-27 16:10:46.643 [TID: N/A] [http-nio-8100-exec-1] DEBUG c.m.k.forlan.mapper.KsBDao.insert -<==    Updates: 1
2022-10-27 16:10:46.670 [TID: N/A] [http-nio-8100-exec-1] INFO  c.m.k.common.config.ControllerAspect -request(2666eff88910474a8f0a921c5da76454)end, cost 459ms

数据成功入库
在这里插入图片描述
在这里插入图片描述

注意:@SpringBootTest需要加上参数(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT),不然会出现下面错误

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8100/kaoshi/forlan/test/add": Connect to localhost:8100 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect; nested exception is org.apache.http.conn.HttpHostConnectException: Connect to localhost:8100 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect

原理

官方文档说明

官方文档说明地址
43.3 Testing Spring Boot applications

@SpringBootTest属性webEnvironment

  • Mock(默认) : 加载WebApplicationContext 并提供模拟的web环境 Servlet环境,使用此批注时,不会启动嵌入式服务器
  • RANDOM_PORT : 加载WebServerApplicationContext 并提供真实的web环境,嵌入式服务器, 监听端口是随机的
  • DEFINED_PORT : 加载WebServerApplicationContext并提供真实的Web环境,嵌入式服务器启动并监听定义的端口(来自 application.properties或默认端口 8080)
  • NONE : 使用SpringApplication加载ApplicationContext 但不提供任何Web环境

If your test is , it will rollback the transaction at the end of each test method by default. If you’re using this arrangement in combination with either or , any transaction initiated on the server won’t rollback as the test is running in a different thread than the server processing.@Transactional RANDOM_PORT DEFINED_PORT

大概意思就是,在单元测试中,加了@Transactional注解,默认情况下,它将在每个测试方法结束时回滚事务。当我们使用RANDOM_PORT或DEFINED_PORT任一作为运行环境,服务器上启动的任何事务都不会回滚,因为测试运行在不同线程中。

posted @ 2022-10-20 12:00  程序员Forlan  阅读(1020)  评论(0编辑  收藏  举报