Liquibase测试数据迁移触发邮箱唯一键冲突,测试启动失败求助
Liquibase预加载测试数据导致Testcontainers启动失败(唯一键冲突)
核心报错信息
Caused by: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/inserts/insert_test_user.yaml::2::Jakub.Kolacz: Reason: liquibase.exception.DatabaseException: ERROR: duplicate key value violates unique constraint "user_email_key" Details: Key (email)=(john.doe@example.com) already exists. [Failed SQL: (0) INSERT INTO public.users (id, first_name, last_name, email, password, city, zip_code, street, building_number, flat_number) VALUES ('3', 'John', 'Doe', 'john.doe@example.com', 'password123', 'Warsaw', '00-001', 'Main St', '123', '45A')]
测试目标接口
@PostMapping public ResponseEntity<UserEntity> createUser(@RequestBody UserDto user) { try { return ResponseEntity.status(HttpStatus.CREATED).body(userService.save(user)); } catch (Exception e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); } }
使用的技术栈
- Spock 测试框架
- Testcontainers(PostgreSQL)模拟测试数据库
- Liquibase 数据库版本管理工具
运行测试时出现Failed to load ApplicationContext错误,根源是Liquibase执行变更集时触发唯一键冲突,导致应用上下文加载失败。
测试代码
class UserControllerITest extends BaseIntegrationTest { @Autowired UserRepository userRepository def "should return bad request when posted user's email is not unique"() { given: "a user and address DTO" def addressEntity = new AddressEntity( city: "Warsaw", zipCode: "00-001", street: "Main St", buildingNumber: "123", flatNumber: "45A" ) def userDto = new UserEntity( id: 2L, firstName: "John", lastName: "Doe", email: "john.doe@example.com", password: "password123", address: addressEntity ) def addressDto2 = new AddressDto( city: "Warsaw", zipCode: "00-001", street: "Main St", buildingNumber: "123", flatNumber: "45A" ) def userDto2 = new UserDto( id: 3L, firstName: "John", lastName: "Doe", email: "john.doe@example.com", password: "password123", address: addressDto2 ) when: "create user endpoint is called" //userRepository.save(userDto) ResponseEntity response = restTemplate.postForEntity(addressToUseForTests + "/api/users", userDto2, UserDto) then: "response is 400 Bad request" response.statusCode == HttpStatus.BAD_REQUEST }
基础集成测试类配置
@Testcontainers @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestPropertySource(locations = "classpath:application-test.properties") abstract class BaseIntegrationTest extends Specification { @LocalServerPort protected int port @Shared protected String addressToUseForTests @Autowired protected TestRestTemplate restTemplate @Container PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:15.3") }
测试环境Liquibase配置
databaseChangeLog: - includeAll: path: db/changelog/changeset/ - includeAll: path: db/changelog/inserts/
注:第一个路径指向建表变更集,第二个路径指向测试数据插入脚本。
完整报错栈
Failed to load ApplicationContext for [WebMergedContextConfiguration@73852720 testClass = com.honeybadgersoftware.cheappy.controller.UserControllerITest, locations = [], classes = [com.honeybadgersoftware.cheappy.CheappyApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = ["classpath:application-test.properties"], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true", "server.port=0"], contextCustomizers = ... Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [org.springframework.boot.autoconfigure.liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/inserts/insert_test_user.yaml::2::Jakub.Kolacz: Reason: liquibase.exception.DatabaseException: ERROR: duplicate key value violates unique constraint "user_email_key" Details: Key (email)=(john.doe@example.com) already exists. [Failed SQL: (0) INSERT INTO public.users ... Caused by: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/inserts/insert_test_user.yaml::2::Jakub.Kolacz: Reason: liquibase.exception.DatabaseException: ERROR: duplicate key value violates unique constraint "user_email_key" Details: Key (email)=(john.doe@example.com) already exists. [Failed SQL: (0) INSERT INTO public.users ... Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "user_email_key" Szczegóły: Key (email)=(john.doe@example.com) already exists.
调试情况
移除Liquibase插入脚本,改为在测试中通过userRepository.save()手动插入数据时,能正常触发数据库层唯一键异常,测试可以正常执行:
2023-08-11T00:52:59.968+02:00 ERROR 9180 --- [ Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: duplicate key value violates unique constraint "user_email_key" Szczegóły: Key (email)=(john.doe@example.com) already exists. could not execute statement [ERROR: duplicate key value violates unique constraint "user_email_key" Szczegóły: Key (email)=(john.doe@example.com) already exists.] [update users setbuilding_number=?,city=?,flat_number=?,street=?,zip_code=?,email=?,first_name=?,last_name=?,passwo rd=? where id=?]; SQL [update users setbuilding_number=?,city=?,flat_number=?,street=?,zip_code=?,email=?,first_name=?,last_name=?,passwo rd=? where id=?]; constraint [user_email_key] org.springframework.dao.DataIntegrityViolationException: could not execute statement [ERROR: duplicate key value violates unique constraint "user_email_key"
手动在本地环境测试接口也能得到正常结果,但使用Liquibase预加载测试数据时,测试启动就失败,未找到解决方案,寻求帮助。
内容的提问来源于stack exchange,提问作者Jakub Kołacz
相关产品推荐
相关产品推荐

