Spring 引导测试 - 没有合格的 Bean 异常
Spring Boot Testing - No Qualifying Bean Exception
在尝试使用 Spring 引导框架进行一些测试时,我 运行 遇到了寻找测试单元所依赖的 Bean 的问题。
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authServerApplication': Unsatisfied dependency expressed through field 'passwordEncoder'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.crypto.password.PasswordEncoder' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
我的测试class:
@RunWith(SpringRunner.class)
@DataJpaTest
@SpringBootTest
public class UserDetailsTest {
@Autowired
private TestEntityManager entityManager;
// @MockBean
// private PasswordEncoder passwordEncoder;
@Autowired
private UserRepository userRepo;
@Test
public void test() {
OAuthUser user = null;
this.entityManager.persist(new OAuthUser("Kelly", "Marchewa", "kmarchewa", "password"));
user = userRepo.findByUserName("kmarchewa");
System.out.println(user.getPassword());
assertThat(user.getUserName()).isEqualTo("kmarchewa");
}
}
如果我取消对@MockBean 部分的注释,代码将可以正常编译。但是,我也想测试存储库编码和解码密码的能力。根据我对 the documentation 的理解,@SpringBootTest 注释应该能够自动 "pick-up" @Configuration classes。我有一个主要的@SpringBootApplication:
@SpringBootApplication
public class AuthServerApplication {
@Autowired
private PasswordEncoder passwordEncoder;
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class, args);
}
@Bean
public CommandLineRunner demo(UserRepository repository) {
return(args) -> {
OAuthUser user = new OAuthUser();
user.setFirstName("Kelly");
user.setLastName("Marchewa");
user.setPassword(passwordEncoder.encode("Admin"));
user.setUserName("Admin");
// repository.save(user);
};
}
}
此 Spring 引导应用程序依赖于其他三个 @Configuration classes:AppConfig、SecurityConfig 和 AuthServerConfig。对于这个问题,SecurityConfig 和 AppConfig classes 是相关的(它们包括对 PasswordEncoder bean 的引用)。
AppConfig(部分)
@Configuration
public class AppConfig {
@Value("${spring.datasource.url}")
private String datasourceUrl;
@Value("${spring.datasource.driverClassName}")
private String dbDriverClassName;
@Value("${spring.datasource.username}")
private String dbUsername;
@Value("${spring.datasource.password}")
private String dbPassword;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/// more code here
}
安全配置:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private OAuthUserDetailsService userService;
@Autowired
private PasswordEncoder passwordEncoder;
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
// Hash password
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService)
.passwordEncoder(passwordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic()
.realmName("test")
.and()
.csrf()
.disable();
}
}
UserRepository class 非常简单:
public interface UserRepository extends CrudRepository<OAuthUser, Long> {
public OAuthUser findByUserName(String name);
}
如何确保为我的测试找到所有必需的 bean?
谢谢。
编辑:
如果我尝试在我的测试中 @Autowire bean,我会收到同样的错误 class。
@Autowired
private PasswordEncoder passwordEncoder;
问题是 @DataJpaTest
这个注解应该只用于数据存储库测试而不是完全集成(这是你正在做的)因为它只在上下文中创建持久性 bean 而不是所有的 bean (找不到bean的原因)。您需要做的是仅使用 @SpringBootTest
并将 h2
声明为测试依赖项,这样将使用内存数据库
创建您的应用程序的完整重建
在尝试使用 Spring 引导框架进行一些测试时,我 运行 遇到了寻找测试单元所依赖的 Bean 的问题。
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authServerApplication': Unsatisfied dependency expressed through field 'passwordEncoder'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.crypto.password.PasswordEncoder' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
我的测试class:
@RunWith(SpringRunner.class)
@DataJpaTest
@SpringBootTest
public class UserDetailsTest {
@Autowired
private TestEntityManager entityManager;
// @MockBean
// private PasswordEncoder passwordEncoder;
@Autowired
private UserRepository userRepo;
@Test
public void test() {
OAuthUser user = null;
this.entityManager.persist(new OAuthUser("Kelly", "Marchewa", "kmarchewa", "password"));
user = userRepo.findByUserName("kmarchewa");
System.out.println(user.getPassword());
assertThat(user.getUserName()).isEqualTo("kmarchewa");
}
}
如果我取消对@MockBean 部分的注释,代码将可以正常编译。但是,我也想测试存储库编码和解码密码的能力。根据我对 the documentation 的理解,@SpringBootTest 注释应该能够自动 "pick-up" @Configuration classes。我有一个主要的@SpringBootApplication:
@SpringBootApplication
public class AuthServerApplication {
@Autowired
private PasswordEncoder passwordEncoder;
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class, args);
}
@Bean
public CommandLineRunner demo(UserRepository repository) {
return(args) -> {
OAuthUser user = new OAuthUser();
user.setFirstName("Kelly");
user.setLastName("Marchewa");
user.setPassword(passwordEncoder.encode("Admin"));
user.setUserName("Admin");
// repository.save(user);
};
}
}
此 Spring 引导应用程序依赖于其他三个 @Configuration classes:AppConfig、SecurityConfig 和 AuthServerConfig。对于这个问题,SecurityConfig 和 AppConfig classes 是相关的(它们包括对 PasswordEncoder bean 的引用)。
AppConfig(部分)
@Configuration
public class AppConfig {
@Value("${spring.datasource.url}")
private String datasourceUrl;
@Value("${spring.datasource.driverClassName}")
private String dbDriverClassName;
@Value("${spring.datasource.username}")
private String dbUsername;
@Value("${spring.datasource.password}")
private String dbPassword;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/// more code here
}
安全配置:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private OAuthUserDetailsService userService;
@Autowired
private PasswordEncoder passwordEncoder;
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
// Hash password
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService)
.passwordEncoder(passwordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic()
.realmName("test")
.and()
.csrf()
.disable();
}
}
UserRepository class 非常简单:
public interface UserRepository extends CrudRepository<OAuthUser, Long> {
public OAuthUser findByUserName(String name);
}
如何确保为我的测试找到所有必需的 bean? 谢谢。
编辑:
如果我尝试在我的测试中 @Autowire bean,我会收到同样的错误 class。
@Autowired
private PasswordEncoder passwordEncoder;
问题是 @DataJpaTest
这个注解应该只用于数据存储库测试而不是完全集成(这是你正在做的)因为它只在上下文中创建持久性 bean 而不是所有的 bean (找不到bean的原因)。您需要做的是仅使用 @SpringBootTest
并将 h2
声明为测试依赖项,这样将使用内存数据库