测试类中使用@PostConstruct导致其被多次调用的问题求助
我懂你的痛点——@WithUserDetails需要从数据库捞取用户,但测试启动时数据库里还没这个用户,肯定会直接报错对吧?我之前也碰到过一模一样的场景,给你几个实际用过的可行方案:
方案一:JUnit 4 @BeforeClass 配合 Spring 上下文创建用户
因为你的测试类是抽象类且用了 JUnit 4 的 @RunWith(SpringRunner.class),可以在抽象类里加一个静态初始化方法,手动从 Spring 上下文拿到用户相关的 Repository/Service,提前创建好测试用户:
@RunWith(value = SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc @WithUserDetails(value = "email@address.com") public abstract class IntegrationTests { @Autowired private MockMvc mockMvc; // 静态变量存上下文,用来获取Repository实例 private static ApplicationContext applicationContext; // 把Spring上下文注入到静态变量中 @Autowired public void setApplicationContext(ApplicationContext context) { applicationContext = context; } @BeforeClass public static void setupTestUser() { UserRepository userRepository = applicationContext.getBean(UserRepository.class); // 先判断用户是否存在,避免重复插入 if (userRepository.findByEmail("email@address.com") == null) { User testUser = new User(); testUser.setEmail("email@address.com"); // 注意:密码必须和Spring Security的加密规则一致,不能存明文 PasswordEncoder encoder = applicationContext.getBean(PasswordEncoder.class); testUser.setPassword(encoder.encode("test123456")); testUser.setUsername("testUser"); // 补充其他必要字段 userRepository.save(testUser); } } // 你的测试方法... }
方案二:自定义 TestExecutionListener 解耦初始化逻辑
如果觉得静态方法的写法有点别扭,可以自定义一个测试执行监听器,在测试类启动前自动完成用户创建,代码更解耦:
首先写一个自定义监听器:
public class TestUserSetupListener extends AbstractTestExecutionListener { @Override public void beforeTestClass(TestContext testContext) throws Exception { super.beforeTestClass(testContext); ApplicationContext context = testContext.getApplicationContext(); UserRepository userRepository = context.getBean(UserRepository.class); if (userRepository.findByEmail("email@address.com") == null) { User testUser = new User(); testUser.setEmail("email@address.com"); PasswordEncoder encoder = context.getBean(PasswordEncoder.class); testUser.setPassword(encoder.encode("test123456")); testUser.setUsername("testUser"); userRepository.save(testUser); } } }
然后在你的抽象测试类上绑定这个监听器:
@RunWith(value = SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc @WithUserDetails(value = "email@address.com") // 关键:MERGE_WITH_DEFAULTS 会保留Spring默认的监听器,避免功能失效 @TestExecutionListeners(listeners = TestUserSetupListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) public abstract class IntegrationTests { @Autowired private MockMvc mockMvc; // 你的测试方法... }
额外建议:测试后清理数据
不管用哪种方案,都建议在测试结束后清理测试用户,避免污染数据库。比如在方案一中添加:
@AfterClass public static void cleanupTestUser() { UserRepository userRepository = applicationContext.getBean(UserRepository.class); User testUser = userRepository.findByEmail("email@address.com"); if (testUser != null) { userRepository.delete(testUser); } }
内容的提问来源于stack exchange,提问作者matthew.kempson
相关产品推荐
相关产品推荐

