Spring Boot集成测试无法Autowired注入Component标注Bean的问题
问题
编写Spring Boot集成测试实现数据库记录创建与读取功能时,运行测试出现如下错误:
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.914 s <<< FAILURE! -- in com.juno.databasedemo.dao.AuthorDaoImplIntegrationTest com.juno.databasedemo.dao.AuthorDaoImplIntegrationTest.testThatAuthorCanBeCreatedAndRecalled -- Time elapsed: 0.057 s <<< ERROR! org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [com.juno.dao.impl.AuthorDaoImpl underTest] in constructor [public com.juno.databasedemo.dao.AuthorDaoImplIntegrationTest(com.juno.dao.impl.AuthorDaoImpl)]: No qualifying bean of type 'com.juno.dao.impl.AuthorDaoImpl' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} at java.base/java.util.Optional.orElseGet(Optional.java:364) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.juno.dao.impl.AuthorDaoImpl' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1880) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1406) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveDependency(AbstractAutowireCapableBeanFactory.java:471) at org.springframework.beans.factory.annotation.ParameterResolutionDelegate.resolveDependency(ParameterResolutionDelegate.java:136) at org.springframework.test.context.junit.jupiter.SpringExtension.resolveParameter(SpringExtension.java:342) ... 3 more
相关代码如下:
AuthorDaoImplIntegrationTest.java
package com.juno.databasedemo.dao; import static org.assertj.core.api.Assertions.*; import java.util.Optional; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; import com.juno.dao.impl.AuthorDaoImpl; import com.juno.databasedemo.TestDataUtil; import com.juno.domain.Author; @SpringBootTest @ExtendWith(SpringExtension.class) class AuthorDaoImplIntegrationTest { private AuthorDaoImpl underTest; @Autowired public AuthorDaoImplIntegrationTest(AuthorDaoImpl underTest) { this.underTest = underTest; } @Test void testThatAuthorCanBeCreatedAndRecalled() { Author author = TestDataUtil.createAuthorForTesting(); // Test Author Creation underTest.create(author); // Test Author Recall Optional<Author> result = underTest.findOne(author.getId()); // Assertion assertThat(result).isPresent(); assertThat(result.get()).isEqualTo(author); } }
AuthorDaoImpl.java
package com.juno.dao.impl; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Optional; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Component; import com.juno.dao.AuthorDao; import com.juno.domain.Author; @Component public class AuthorDaoImpl implements AuthorDao { private final JdbcTemplate jdbcTemplate; public AuthorDaoImpl(final JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void create(Author author) { this.jdbcTemplate.update("INSERT INTO author(id, name, age) values (?, ?, ?)", author.getId(), author.getName(), author.getAge()); } @Override public Optional<Author> findOne(int id) { List<Author> results = jdbcTemplate.query( "SELECT id, name, age FROM author where id = ? LIMIT = 1", new AuthorRowMapper(), id); return results.stream().findFirst(); } public static class AuthorRowMapper implements RowMapper<Author> { @Override public Author mapRow(ResultSet rs, int rowNum) throws SQLException { Author author = Author.builder() .id(rs.getInt("id")) .name(rs.getString("name")) .age(rs.getInt("age")) .build(); return author; } } }
DatabaseConfig.java
package com.juno.config; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; @Configuration public class DatabaseConfig { @Bean public JdbcTemplate jdbcTemplate(final DataSource dataSource) { JdbcTemplate restTemplate = new JdbcTemplate(dataSource); return restTemplate; } }
尝试在DatabaseConfig中为DataSource和AuthorDaoImpl创建Bean,但问题依旧。
解决方案
1. 修复Spring组件扫描范围问题
错误核心是Spring容器找不到AuthorDaoImpl的Bean,原因是组件扫描范围不包含com.juno.dao.impl包:
- Spring Boot默认扫描启动类所在包及其子包。如果启动类在
com.juno.databasedemo下,com.juno.dao不在默认扫描范围内,导致@Component注解的AuthorDaoImpl无法被Spring识别。
解决方法(选其一即可):
- 将启动类移到
com.juno包下,所有子包都会被自动扫描。 - 在启动类上添加
@ComponentScan(basePackages = "com.juno"),显式指定全局扫描范围。 - 在测试类的
@SpringBootTest注解中指定需要加载的Bean:@SpringBootTest(classes = {DatabaseConfig.class, AuthorDaoImpl.class})。
2. 修正SQL语法错误
AuthorDaoImpl的findOne方法中SQL存在语法错误:LIMIT = 1应改为LIMIT 1,否则后续测试会出现SQL执行失败问题。修正后的代码:
@Override public Optional<Author> findOne(int id) { List<Author> results = jdbcTemplate.query( "SELECT id, name, age FROM author where id = ? LIMIT 1", new AuthorRowMapper(), id); return results.stream().findFirst(); }
3. (可选)简化测试类注入方式
测试类的构造器注入逻辑没问题,也可以直接使用字段注入简化代码:
@SpringBootTest @ExtendWith(SpringExtension.class) class AuthorDaoImplIntegrationTest { @Autowired private AuthorDaoImpl underTest; // 测试方法保持不变... }
内容的提问来源于stack exchange,提问作者Juno Lee
相关产品推荐
相关产品推荐

