当我们在Java项目中,使用dbunit做UT测试时,有可能会发生下面AmbiguousTableNameException的错误。
UT测试类代码:
@SpringBootTest(classes = ServiceTestApplication.class) @ActiveProfiles("test") @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, MockitoTestExecutionListener.class, DbUnitTestExecutionListener.class }) @ComponentScan(value = "com.xxxx") @AutoConfigureMockMvc public class TestControllerTest { @Autowired private MockMvc mockMvc; @Test @DatabaseSetup(value = { "classpath:com/test_102N.xls" }) @DatabaseTearDown(value = { "classpath:databaseTearDown/DatabaseTearDown.xls" }) void test_102N() throws Exception { // ヘッダーの情報の設定 org.springframework.http.HttpHeaders headers = new HttpHeaders(); headers.add("x-user-id", "123"); headers.add("x-system-id", "456"); MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.put("testKbn", Collections.singletonList("1")); // リクエストを実行する MvcResult mvcResult = mockMvc.perform(get("/test").queryParams(params).headers(headers)).andReturn(); String actualContent = mvcResult.getResponse().getContentAsString(Charset.forName("UTF-8")); // リターンステータスが正しいかどうかを判断する int status = mvcResult.getResponse().getStatus(); assertEquals(HttpStatus.OK.value(), status); } }
application-test.yaml:
注意:这里用jdbc:postgresql://localhost:5432/postgres?currentSchema=ut_db 指定连接的Schema不起作用
spring.datasource: url: jdbc:postgresql://localhost:5432/postgres username: dummy password: dummyvalue driver-class-name: org.postgresql.Driver spring.main.allow-circular-references: true logging: level: com.jtb.nucleus: debug
异常信息:

分析原因
当一个实例下存在多个数据库,而且这个数据库中存在相同名称的表,Dbunit会报一个不能区分是哪个表的错误。
在数据库中存在两个同名的数据表,同名->不区分大小写的同名,比如user和USER也是同名的,特别的是这两个同名数据表可能是存在本地中两个不同的Schema中。
在没有指定Schema的情况下,DbUnit会默认扫描整个实例。

解决方法
- 重命名你当前项目的数据表,使其不与数据库中其他数据表重名,如user命名为t_user。
- 删除其他数据库中的重名数据表,使你当前数据表命名唯一。
- 假如在连接数据库时未指定到哪个具体的schema,那么就指定schema。
指定schema的例子
创建db连接Configuration类(注意TestConfiguration注解,因为该配置只想在Test类有效)
package com.xxx.config; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.github.springtestdbunit.bean.DatabaseConfigBean; import com.github.springtestdbunit.bean.DatabaseDataSourceConnectionFactoryBean; //@Configuration @TestConfiguration public class DbUnitConfig { @Bean("dbUnitConnection") DatabaseDataSourceConnectionFactoryBean getTestConnection(DataSource dataSource) { DatabaseDataSourceConnectionFactoryBean bean = new DatabaseDataSourceConnectionFactoryBean(); bean.setDataSource(dataSource); DatabaseConfigBean databaseConfigBean = new DatabaseConfigBean(); databaseConfigBean.setAllowEmptyFields(true); bean.setDatabaseConfig(databaseConfigBean); bean.setSchema("ut_db"); return bean; } }
在测试类通过DbUnitConfiguration指定数据源
@SpringBootTest(classes = ServiceTestApplication.class) @ActiveProfiles("test") @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, MockitoTestExecutionListener.class, DbUnitTestExecutionListener.class }) @ComponentScan(value = "com.xxxx") @AutoConfigureMockMvc @DbUnitConfiguration(dataSetLoader = XlsDataSetLoader.class, databaseConnection = { "dbUnitConnection" }) public class TestControllerTest { @Autowired private MockMvc mockMvc; @Test @DatabaseSetup(value = { "classpath:com/test_102N.xls" }) @DatabaseTearDown(value = { "classpath:databaseTearDown/DatabaseTearDown.xls" }) void test_102N() throws Exception { // ヘッダーの情報の設定 org.springframework.http.HttpHeaders headers = new HttpHeaders(); headers.add("x-user-id", "123"); headers.add("x-system-id", "456"); MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.put("testKbn", Collections.singletonList("1")); // リクエストを実行する MvcResult mvcResult = mockMvc.perform(get("/test").queryParams(params).headers(headers)).andReturn(); String actualContent = mvcResult.getResponse().getContentAsString(Charset.forName("UTF-8")); // リターンステータスが正しいかどうかを判断する int status = mvcResult.getResponse().getStatus(); assertEquals(HttpStatus.OK.value(), status); } }
补充
另外,DbUnitConfig作为TestConfiguration,会在测试类启动时被扫描加载,会创建 @Bean(“dbUnitConnection”),但是这个Bean需要创建一个DataSource dataSource连接,但是假如我们的测试工程不需要连接数据库,而没有设置数据库的配置,启动测试类时会导致出下面的错误。
Description: Parameter 0 of method getTestConnection in DbUnitConfig required a bean of type 'javax.sql.DataSource' that could not be found. Action: Consider defining a bean of type 'javax.sql.DataSource' in your configuration.
解决方法是我们可以在测试类加上,来mock一个DataSource
@MockBean DataSource dataSource;
完整代码
@SpringBootTest(classes = ServiceTestApplication.class) @ActiveProfiles("test") @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, MockitoTestExecutionListener.class, DbUnitTestExecutionListener.class }) @ComponentScan(value = "com.xxxx") @AutoConfigureMockMvc @DbUnitConfiguration(dataSetLoader = XlsDataSetLoader.class, databaseConnection = { "dbUnitConnection" }) public class TestControllerTest { // 这部分很重要 @MockBean DataSource dataSource; @Autowired private MockMvc mockMvc; @Test @DatabaseSetup(value = { "classpath:com/test_102N.xls" }) @DatabaseTearDown(value = { "classpath:databaseTearDown/DatabaseTearDown.xls" }) void test_102N() throws Exception { // ヘッダーの情報の設定 org.springframework.http.HttpHeaders headers = new HttpHeaders(); headers.add("x-user-id", "123"); headers.add("x-system-id", "456"); MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.put("testKbn", Collections.singletonList("1")); // リクエストを実行する MvcResult mvcResult = mockMvc.perform(get("/test").queryParams(params).headers(headers)).andReturn(); String actualContent = mvcResult.getResponse().getContentAsString(Charset.forName("UTF-8")); // リターンステータスが正しいかどうかを判断する int status = mvcResult.getResponse().getStatus(); assertEquals(HttpStatus.OK.value(), status); } }