Issue
Bit of a Spring Boot rookie here, so appreciate any help!
I've built a Spring Boot application with JDBCTemplate that runs and functions normally with no errors or exceptions.
I have produced a test class and want to use @JdbcTest to test my Dao objects. However, every time I run a test, I get java.lang.IllegalStateException: Failed to load ApplicationContext
. There seems to be an issue with my Controller classes. This IllegalStateException is caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'assetApiController' defined in file [/PATH/TO/FILE]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
My test class:
@JdbcTest
@Sql({"schema.sql", "test-data.sql"})
class AssetApiControllerTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
AssetDao assetDao;
@Test
void delete() throws DataAccessException {
assetDao.setJdbcTemplate(jdbcTemplate);
assetDao.deleteByPk(new AssetKey(1), null);
assertEquals(0, assetDao.selectAll(null).size());
}
}
My Dao:
@Repository("assetDao")
public class AssetDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void deleteByPk (AssetKey assetKey, Connection con) throws DataAccessException {
jdbcTemplate.update("DELETE FROM Asset WHERE id = ? ", assetKey.getId());
}
}
My controller class:
@Controller
public class AssetApiController implements AssetApi {
private static final Logger log = LoggerFactory.getLogger(AssetApiController.class);
private final ObjectMapper objectMapper;
private final HttpServletRequest request;
@Autowired
private AssetDao assetDao;
@org.springframework.beans.factory.annotation.Autowired
public AssetApiController(ObjectMapper objectMapper, HttpServletRequest request) {
this.objectMapper = objectMapper;
this.request = request;
}
@Override
public ResponseEntity<Void> delete(@RequestBody AssetKey assetKey) {
try{
assetDao.deleteByPk(assetKey, null);
} catch (ApplicationException e) {
log.warn(e.getFormattedMessage(), e);
return new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
My JacksonConfig:
@Configuration
public class JacksonConfiguration {
@Bean
@ConditionalOnMissingBean(ThreeTenModule.class)
ThreeTenModule threeTenModule() {
ThreeTenModule module = new ThreeTenModule();
module.addDeserializer(Instant.class, CustomInstantDeserializer.INSTANT);
module.addDeserializer(OffsetDateTime.class, CustomInstantDeserializer.OFFSET_DATE_TIME);
module.addDeserializer(ZonedDateTime.class, CustomInstantDeserializer.ZONED_DATE_TIME);
return module;
}
}
Two more points that could help diagnose this:
I followed this answer: https://stackoverflow.com/a/32842962/11853066 . And it fixed the problem, but then I got an equivalent error for HttpServletRequest:
No qualifying bean found for dependency [javax.servlet.http.HttpServletRequest]
. So the underlying issue had not been addressed.When I try and produce tests for an entity other than Asset (e.g. 'Supplier' with SupplierController, SupplierDao), I get the same error:
Error creating bean with name 'assetApiController' defined in file
. This must be because assetApiController is the first controller to be scanned because of alphabetical order?
Solution
Hi :) What spring boot version do you have? Overall seems like a generic application context configuration issue. I would rather refactor a bit before starting with the test. It is very good to use the @Autowired constructors in Spring Boot. It will make your life much easier when you want to test.
- Refactor the DAO class.
@Repository("assetDao")
public class AssetDao {
private final JdbcTemplate jdbcTemplate;
//handy you can put also validation here if all is good
//we will use it later in the test
@Autowired
public AssetDao (JdbcTemplate jdbcTemplate){
jdbcTemplate = jdbcTemplate;
}
public void deleteByPk (AssetKey assetKey, Connection con) throws DataAccessException
{
jdbcTemplate.update("DELETE FROM Asset WHERE id = ? ", assetKey.getId());
}
}
- Now the test... seems you test the DAO via the Controller? What happens is the whole application context wants to load togather with your dao. You want to @Autowire. You have to init the spring context inside your test etc...there fore the errors you get. If you want to focus on the DAO you could do:
@JdbcTest
@Sql({"schema.sql", "test-data.sql"})
class AssetDaoTest {
private AssetDao assetDao;
private JdbcTemplate template;
@Before
public void setup() {
template = new JdbcTemplate();
assetDao = new AssetDao(template);
}
@Test
void delete() throws DataAccessException {
assetDao.deleteByPk(new AssetKey(1), null);
assertEquals(0, assetDao.selectAll(null).size());
}
}
Give it a try, did not run it on my PC yet.
Answered By - Daniela Todorova