How to implement the Unit Test for DatasourceConfiguration with HikariConfig - java

Here is my DatasourceConfiguration.java file. It's a configuration file, to set up the Hikari config and the QuartzDatasource is built based on the Hikari config.
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class DatasourceConfiguration {
#Value("${spring.datasource.scheduler.jdbcUrl}")
private String jdbcUrl;
#Value("${spring.datasource.scheduler.username}")
private String userName;
#Value("${spring.datasource.scheduler.driverClassname}")
private String driverClassname;
#Value("${spring.datasource.scheduler.password}")
private String password;
#Bean
public HikariConfig hikariConfig() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(driverClassname);
hikariConfig.setJdbcUrl(jdbcUrl);
hikariConfig.setUsername(userName);
hikariConfig.setPassword(password);
return hikariConfig;
}
#Bean(name = "quartzDataSource")
#QuartzDataSource
public DataSource dataSource() {
return new HikariDataSource(hikariConfig());
}
}
And here is my DatasourceConfigurationTest.java file:
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.context.ApplicationContext;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.sql.DataSource;
import java.sql.SQLException;
import static org.assertj.core.api.Assertions.assertThat;
#ExtendWith(SpringExtension.class)
#TestPropertySource(locations = "classpath:application.properties")
#ContextConfiguration(classes = DatasourceConfiguration.class)
class DatasourceConfigurationTest {
#Autowired
private ApplicationContext context;
#Test
void validateConfiguration() throws SQLException {
assertThat(this.context.getBeansOfType(DataSource.class)).hasSize(1);
HikariDataSource dataSource = this.context.getBean(HikariDataSource.class);
assertThat(dataSource.getConnection().getMetaData().getURL()).isEqualTo("jdbc:h2:mem:simple");
assertThat(dataSource.getMaximumPoolSize()).isEqualTo(42);
}
}
When I run the Unit Test, I recognized that it failed to load ApplicationContext.
Here is the log:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getHikariConfig' defined in com.gm.gcc.vas.utility.messageretry.config.DatasourceConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariConfig]: Factory method 'getHikariConfig' threw exception; nested exception is java.lang.RuntimeException: Failed to load driver class test-driver-classname in either of HikariConfig class loader or Thread context classloader
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:486)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariConfig]: Factory method 'getHikariConfig' threw exception; nested exception is java.lang.RuntimeException: Failed to load driver class test-driver-classname in either of HikariConfig class loader or Thread context classloader
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
... 87 common frames omitted
Caused by: java.lang.RuntimeException: Failed to load driver class test-driver-classname in either of HikariConfig class loader or Thread context classloader
at com.zaxxer.hikari.HikariConfig.setDriverClassName(HikariConfig.java:491)
at com.gm.gcc.vas.utility.messageretry.config.DatasourceConfiguration.getHikariConfig(DatasourceConfiguration.java:37)
... 88 common frames omitted
Here is the properties file:
spring.datasource.scheduler.driverClassname=test-driver-classname
spring.datasource.scheduler.jdbcUrl=jdbc:test-url
spring.datasource.scheduler.password=test-password
spring.datasource.scheduler.username=test-username
As my understanding, when trying to create a connection, it failed because of dummy values.
I just wonder if my approach to do the unit test is correct or not, if yes, how can I fix the ApplicationContext error? Else, what is the correct way to ensure 100% converage.

If you want to execute integration tests, you need a running DB instance to which Spring can connect. Your test is not a unit test as it is extended with the SpringExtension.
You could use an embedded H2 database for your tests, you would then need to add H2 as test dependency in your pom.xml:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
In your application-test.properties:
spring.datasource.scheduler.driverClassname=org.h2.Driver
spring.datasource.scheduler.jdbcUrl=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
spring.datasource.scheduler.password=sa
spring.datasource.scheduler.username=sa
In order to have the application-test.properties loaded for your test you should either replace the #TestPropertySource(locations = "classpath:application.properties") with #ActiveProfiles("test") or reference the file #TestPropertySource(locations = "classpath:application-test.properties").
Another good alternative would be using the Testcontainers project which allows you to run the database that you use in production (like Postgres or MySQL) in a docker container. Here is a good tutorial by Baeldung.

Related

spring boot rest api | no ServletContext Set [duplicate]

I'm triyng to deploy a spring-mvc webapp WAR-package on tomcat. Deploy proccess fails with following error: 'java.lang.IllegalStateException: No ServletContext set'
I guess something wrong with my configuration :(
Tomcat version: 8.5.46
Spring, spring-mvc version: 5.1.9.RELEASE
Example configuration code from spring.io: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet
My webapp initializer:
package com.jbtits.spring.mvc.webac;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class AppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(AppConfig.class);
applicationContext.refresh();
DispatcherServlet servlet = new DispatcherServlet(applicationContext);
ServletRegistration.Dynamic registration = servletContext.addServlet("webac", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
My webapp configuration:
package com.jbtits.spring.mvc.webac;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
#Configuration
#EnableWebMvc
#ComponentScan("com.jbtits.spring.mvc.webac")
public class AppConfig extends WebMvcConfigurationSupport {
}
Thats it, only two beans.
Tomcat failure output:
02-Oct-2019 18:02:52.971 WARNING [http-nio-8081-exec-84] org.springframework.context.support.AbstractApplicationContext.refresh Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
I found the solution: no need to call applicationContext.refresh(); in the org.springframework.web.WebApplicationInitializer#onStartup, cause it will be automaticaly called in the method org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext while servlet loading in the servlet container.
But I use example from spring.io docs: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet . Why they use it that way?
proof

Ehcache 3 spring boot not working in integration test

I need your help please. I integrated Ehcache 3 with springboot 2. everything works perfectly in production. integration tests also work if i execute them manualy ( I'm using Intellij IDE ).
but when i run "mvn install", i get this error :
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'ehCacheManager' defined in class path
resource [com/afklm/belem/payment/config/EhCacheConfiguration.class]:
Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [org.springframework.cache.CacheManager]: Factory method
'ehCacheManager' threw exception; nested exception is
javax.cache.CacheException: A Cache named [currenciesCache] already
exists
CurrencyRepository interface :
#Repository
public interface CurrencyRepository extends JpaRepository<Currency, String> {
/**
* Gets currencies from Cache.
* The real call to the DataBase will be performed if the response is not in cache
*
* #return list of {#link Currency}
*/
#Cacheable(cacheNames = "currenciesCache", key ="#root.methodName", unless = "#result==null or #result.size()==0")
default List<Currency> getCachedCurrencies() {
return findAll();
}
Cache configuration class
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ExpiryPolicyBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.CacheManager;
import javax.cache.Caching;
import java.time.Duration;
import java.util.List;
#EnableCaching
#Configuration
public class EhCacheConfiguration extends CachingConfigurerSupport {
#Bean
public CacheManager ehCacheManager() {
//-> currencies cache configuration
CacheConfiguration<String, List> currenciesCacheConfig =
CacheConfigurationBuilder
.newCacheConfigurationBuilder(String.class, List.class, ResourcePoolsBuilder.heap(1000))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofDays(30)))
.withDefaultEventListenersThreadPool()
.build();
javax.cache.CacheManager cacheManager = Caching.getCachingProvider().getCacheManager();
cacheManager.createCache("currenciesCache", Eh107Configuration.fromEhcacheCacheConfiguration(currenciesCacheConfig));
return new JCacheCacheManager(cacheManager);
}
Thanks for your help; i'm stuck
I think I faced this before when at least one of my tests had the #DirtiesContext annotation which re-initializes the Spring context. I got around it by making a call to the cacheManager.destroyCache method before attempting to create the cache. Can you try adding the statement cacheManager.destroyCache("currenciesCache"); before the cacheManager.createCache statement? Alternatively, you could try initializing the cache manager without the explicit call to the createCache method like it has been done in this Stack Overflow answer

Failed to load ApplicationContext inside the #TestConfiguration class

I'm using #TestConfiguration annotation to define bean provider for the JUnit5 test class.
A test class is annotated with:
#Import(MyTestConfiguration.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT).
package com.example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
#Import(MyTestConfiguration.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MyTest {
...
}
A configuration class is annotated with
#TestConfiguration.
package com.example;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
#TestConfiguration
public class MyTestConfiguration {
#LocalServerPort
private int port;
...
}
I'm trying to inject local server port inside the configuration class with #LocalServerPort annotation, but get a Failed to load ApplicationContext error Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.example.MyTestConfiguration': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'local.server.port' in value "${local.server.port}".
I was using #LocalServerPort successfully in the test class, but decided to move it along with other objects which use it to the MyTestConfiguration class to make test class more clear.
Add #Lazy annotation to the test configuration class. More info here.
From the documentation:
If this annotation is not present on a #Component or #Bean definition,
eager initialization will occur. If present and set to true, the #Bean
or #Component will not be initialized until referenced by another bean
or explicitly retrieved from the enclosing BeanFactory. If present and
set to false, the bean will be instantiated on startup by bean
factories that perform eager initialization of singletons.

Failed to parse configuration class, nested exception is java.io.FileNotFoundException: Could not open resource [/test.properties]

I´m trying to configurate new properties for the test, so I created a test config class:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.scheduling.annotation.EnableAsync;
#Configuration
#EnableAsync
#ComponentScan("ar.com.yo")
#PropertySource("test.properties")
public class TestConfig {
}
Properties file is in src/test/resources/test.properties
and in the test class :
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes = TestConfig.class)
public class InsumoServiceTest {
...
}
when I execute the tests the error is:
Failed to parse configuration class [ar.com.yo.myproject.main.TestConfig]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/test.properties]
It seems that the requested properties can't be found. I would recommend doing this:
is that resolve your problem :
#PropertySource("classpath:test.properties")

"java.lang.IllegalStateException: No ServletContext set" while servlet deploying

I'm triyng to deploy a spring-mvc webapp WAR-package on tomcat. Deploy proccess fails with following error: 'java.lang.IllegalStateException: No ServletContext set'
I guess something wrong with my configuration :(
Tomcat version: 8.5.46
Spring, spring-mvc version: 5.1.9.RELEASE
Example configuration code from spring.io: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet
My webapp initializer:
package com.jbtits.spring.mvc.webac;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class AppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(AppConfig.class);
applicationContext.refresh();
DispatcherServlet servlet = new DispatcherServlet(applicationContext);
ServletRegistration.Dynamic registration = servletContext.addServlet("webac", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
My webapp configuration:
package com.jbtits.spring.mvc.webac;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
#Configuration
#EnableWebMvc
#ComponentScan("com.jbtits.spring.mvc.webac")
public class AppConfig extends WebMvcConfigurationSupport {
}
Thats it, only two beans.
Tomcat failure output:
02-Oct-2019 18:02:52.971 WARNING [http-nio-8081-exec-84] org.springframework.context.support.AbstractApplicationContext.refresh Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
I found the solution: no need to call applicationContext.refresh(); in the org.springframework.web.WebApplicationInitializer#onStartup, cause it will be automaticaly called in the method org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext while servlet loading in the servlet container.
But I use example from spring.io docs: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet . Why they use it that way?
proof

Categories