Spring4 boot unittest #ActiveProfiles causing failed to load applicationContext error - java

I want to provide different database for running unitTest than using default production database. I thought about using profile to solve this issue.
This is spring4 boot project, so everything is annotated.
This is what I am doing:
Under src/main/resources, I put application.properties:
spring.datasource.url=jdbc:postgresql://localhost:5432/services
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.driver-class-name=org.postgresql.Driver
Under src/test/resources, I put application-test.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/services_test
spring.datasource.username=postgres
spring.datasource.password=Hercules1
spring.datasource.driver-class-name=org.postgresql.Driver
Then, I put #ActiveProfiles("test") before test, now when I run the unit test, I immediately meet this error:
java.lang.IllegalStateException: Failed to load ApplicationContext
I googled quite a lot and nothing can solve this error.
Can you point out what's wrong with my solution?
Thanks

Just suffixing -test to application.properties will not make the properties a candidate for the profile that you activate. You need to do the following:
A datasource configuration interface:
public interface DatasourceConfig {
public void setup();
}
Test Datasource configuration:
#Component
#Profile("test")
public class ProductionDatasourceConfig implements DatasourceConfig {
#Override
public void setup() {
// Set up your test datasource
}
}
Production datasource configuration:
#Component
#Profile("prod")
public class ProductionDatasourceConfig implements DatasourceConfig {
#Override
public void setup() {
// Set up your prod datasource
}
}
Activate the profile:
#ActiveProfiles("test")
Inject the datasource based on environment:
#Autowired
DatasourceConfig datasourceConfig;
Beans declared in XML can also be mapped to the profile as below:
<beans profile="dev">
<bean id="devDatasourceConfig" class="org.profiles.DevDatasourceConfig" />
</beans>
<beans profile="prod">
<bean id="productionDatasourceConfig" class="org.profiles.ProductionDatasourceConfig" />
</beans>

Related

Spring override bean of another module

In my multimodule project I have integration tests as seperate module. That tests have application jar added as dependency. Is it possible to override application bean definition from integration tests?
In application I have following Bean (standard java mail sender configuration)
#Configuration
public class MailConfiguration {
#Bean
public JavaMailSender javaMailService() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
//standard mail configuration using properties
}
}
Now all my integration tests extends BaseIntegrationTest that loads test configuration classess
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = { AppTestConfiguration.class, MailTestConfiguration.class})
public class BaseIntegrationTest {
}
And finally in my MailTestConfiguration I define another JavaMailSender
#Primary
#Bean
#Profile(TestProfiles.MAIL_GREEN_SMTP)
public JavaMailSender javaMailService() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setHost("localhost");
javaMailSender.setPort(3025);
return javaMailSender;
}
It is working when I run the tests from application itself. When I run the tests from another module the bean is not overriden.
I am aware that AppConfiguration class defined inside application cannot component scan the integration tests config classes so I also load AppTestConfiguration.
#Configuration
#ComponentScan(basePackages = {"..."})
public class AppTestConfiguration extends AppConfiguration {
}
How to make it work?
Two things you should check:
Is the right Spring profile enabled when you run the tests from another module?
Is the given configuration in the scan path when you run the tests from another module?
Sure I messed up with profiles. In my BaseIntegrationTest I defined active profile based on configuration. I also printed which profile gets resolved there (correct profile name was printed)
#BeforeClass
public static void init() {
System.setProperty(DEFAULT_PROFILES_PROPERTY_NAME, ProfileResolver.getActiveProfiles());
}
After you convinced me that it should work I rechecked the config and found that I also added spring.profiles.active in properties. After deleting this config everything work as expected. The other way is to use AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME instead of AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME

NoSuchBeanDefinitionException after migrate from spring boot 1.5 to 2.0

Today I want to upgrade from spring boot 1.5.7 to 2.0.1 but at runtime spring cannot find any of my spring-beans that are outside of my ApplicationConfiguration class even if I mentioned these package on SpringBootApplication (scanBasePackages) annotation.
These are the properties of the versions of my pom.xml file:
<properties>
<hibernate.version>5.2.10.Final</hibernate.version>
<spring.version>5.0.5.RELEASE</spring.version>
<springSecurity.version>5.0.4.RELEASE</springSecurity.version>
<springBoot.version>2.0.1.RELEASE</springBoot.version>
<jackson.version>2.9.1</jackson.version>
<slf4j.version>1.7.13</slf4j.version>
<logback.version>1.1.3</logback.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
This is my ApplicationConfiguration class:
package t.i.config;
#SpringBootApplication(scanBasePackages = {
"t.i.DAO",
"t.i.SERVICES",
"t.i.config"
})
#EnableAutoConfiguration(exclude = {
/*HibernateJpaAutoConfiguration.class*/ // old spring-boot 1.5 class
})
#PropertySource({ "classpath:application.mssql.properties" })
#EnableCaching
#EnableTransactionManagement
public class ApplicationConfiguration {
#Primary
#Autowired
#Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {
... code there that need MultiTenantConnectionProvider ...
}
}
SessionFactory bean need MultiTenantConnectionProvider which exists in package t.i.config.multitenancy:
package t.i.config.multitenancy;
#Component
public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider {
... code there ...
}
Despite the annotation on configuration class:
#SpringBootApplication(scanBasePackages = "t.i.config")
Spring throw exception at runtime:
NoSuchBeanDefinitionException: No qualifying bean of type 'org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider' available
This is an exemple with MultiTenantConnectionProvider but spring does not found any of my beans (repositories and services) if they are not explicitly declared in ApplicationConfiguration file.
Maybe it is not important, but I launch my app as a jar and my class SpringBootApplicationLauncher handle commandlines parameters and context loading:
package t.i;
public class SpringBootApplicationLauncher {
public static void main(String[] args) throws Exception {
SpringApplication springApp = new SpringApplication(new Class[]{t.i.config.ApplicationConfiguration.class});
context = springApp.run(args);
}
}
Where I was wrong ? EDIT: It work thanks to Norbert Bicsi answer.
Since you are defining your own datasource in the multi-tenancy app, you cannot let Spring Boot do its magic of autoconfiguration of data sources and connection to databases as defined in the datasources properties.
You should be excluding the DataSourceAutoConfiguration class.
So you need to add the exclude attribute as follows:
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
I have a recently created Spring Boot 2 (with Hibernate) and Spring Security 5 based database-per-tenant multitenancy web app with complete source code. Take a look and let me know if it helps.
You could try the basic approach where you leave you application class at the root level in your project so the #SpringBootApplication does a component scan of all the packages next to it and all subpackages.
Or you could try with the component scan annotation like:
#ComponentScan(basePackages = {
"t.i.DAO",
"t.i.SERVICES",
"t.i.config"
})
#SpringBootApplication
public class ApiApplication {
// ...
}

Spring Boot Exclude DataSource Configuration

I have a small application that when live, makes a database connection, and stores and persists some data.
I'm currently in the midsts of trying to write some tests, and I'm wanting to completely cut off the database part of the application, and just mock it in the tests.
The Datasource is setup with a configuration class.
#Component
#Configuration
public class DataSourceConfiguration {
#Bean
public DataSource myDataSource() { ... }
}
and a test boostrap that currently looks similar to
#RunWith(SpringRunner.class)
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class MyTest {
}
When running the test, I can see that Spring is trying to instantiate Hibernate, and a connection to the db, I assume because of my config class. How can I stop Spring from doing this?
No Need to use : #EnableAutoConfiguration
We can narrow down the tests to just the web layer by using #WebMvcTest as below,
#RunWith(SpringRunner.class)
#WebMvcTest
public class WebLayerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World")));
}
}
Refer how to test spring application with only web or using complete application context loading : https://spring.io/guides/gs/testing-web/ refer mocking example : http://www.lucassaldanha.com/unit-and-integration-tests-in-spring-boot/

Spring 3 default beans

I am working on a project with multiple spring configuration java classes. Many of them have beans from other config classes autowired in and then injected in the constructors of other beans.
To make this as flexible as possible, I have been using spring profiles to define which implementation of an interface to use in the case where multiple are available.
This works fine, but I was wondering if there was any way with Spring that you could define a default bean?
For example: If no bean of type Foo found on classpath, inject implementation Bar. Else, ignore Bar.
I have looked at this question: Spring 3: Inject Default Bean Unless Another Bean Present, and the solution shown with Java config would work fine if you knew the name of all of the beans, but in my case I will not know what the beans are called.
Does anybody know of a way this can be achieved?
Define the default as, well the default, just make sure that the name of the bean is the same, the one inside the profile will override the default one.
<beans>
<!-- The default datasource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
</bean>
<beans profile="jndi">
<jndi:lookup id="dataSource" jndi-name="jdbc/db" />
</beans>
</beans>
This construct would also work with Java based config.
#Configuration
public DefaultConfig {
#Bean
public DataSource dataSource() { ... }
#Configuration
#Profile("jndi")
public static class JndiConfig {
#Bean
public DataSource dataSource() { ... // JNDI lookup }
}
}
When using java based configuration you can also specify a default and in another configuration add another bean of that type and annotate it with #Primary. When multiple instances are found the one with #Primary should be used.
#Configuration
public DefaultConfig {
#Bean
public DataSource dataSource() { ... }
}
#Configuration
#Profile("jndi")
public class JndiConfig {
#Bean
#Primary
public DataSource jndiDataSource() { ... // JNDI lookup }
}

How to enforce loading order of spring configuration classes?

I'm working with spring-boot on a multi module project (maven). Each module has it's own #Configuration class. Basically I do have the following layout
Module foo-embedded (runs just calls the SpringApplication.run()) method:
#Configuration
#EnableAutoConfiguration
#ComponentScan("de.foobar.rootpackage")
#Import({ApplicationConfig.class, RepositoryConfig.class, SecurityConfig.class})
public class FooApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(FooApplication.class, args);
}
}
Module foo-common (contains all beans and spring-data-jpa initialization config)
#Configuration
#EnableJpaRepositories
#EnableTransactionManagement(entityManagerFactoryRef="entityManagerFactory")
public class RepositoryConfig {
#Bean(destroyMethod = "shutdown")
public DataSource getDataSource() {
// returning a Hikari CP here
}
#Bean(name = "entityManagerFactory") // overriding spring boots default
public EntityManagerFactory getEntityManagerFactory() {
// returning a new LocalEntityManagerFactoryBean here
}
}
Module foo-security (containing spring-securiy configuration and related domain classes), which has a maven dependency on foo-common
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// configuring HTTP security and defining my UserDetailsService Bean
}
When I start the application using the FooApplication class, everything works as expected. The above mentioned UserDetailsServiceImpl get's autowired with my UserRepository which is being created through the #EnableJpaRepositories annotation.
Since I want to write some integration tests I've added a test clss to one of my modules.
Module foo-media (containing some domain related stuff plus test cases for that module)
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {RepositoryConfig.class, SecurityConfig.class})
#WebAppConfiguration
#IntegrationTest
public class DirectoryIntegrationTest {
// my test code
}
When I run the test it seems that the SecurityConfiguration get's loaded before the RepositoryConfig.class does. Since the security config defined the UserServiceImpl which must be autowired, the test fails to start with a
NoSuchBeanDefinitionException telling me: No qualifying bean of type [com.foo.rootpackage.security.repository.UserRepository]
I already tried to add #DependsOn("UserRepository") at the bean definition of UserDetailsService, telling me that spring can't find a bean by that name.
Any hints or help would be greatly appreciated! Thanks in advance!
---- EDIT (since I was asked to provide more code) ----
For testing I do not use the actual RepositoryConfig.class, but have a TestRepositoryConfig.class in the common module. Looking like this
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", basePackages = "de.foobar.rootpackage")
public class TestRepositoryConfig extends RepositoryConfig {
#Bean
#Override
public DataSource getDataSource() {
// returning the ds for testing
}
}
You can use #Order annotation on your configuration classes to define load ordering. But it's strange because Spring should resolve proper order - so please check if you property inject UserRepository in UserDetailsService
So I was able to solve this. As it pointed out it had nothing to do with the loading order of the configuration classes (which was my first thought).
As you may notice, the only Configuration that had a #ComponentScan annotation was the FooApplication.class
Spring was not able to find the Repositories, as it didn't know where to look for. Providing the basePackages attribute like this:
#EnableJpaRepositories(basePackages = "de.foobar.rootpackage")
at the TestRepositoryConfig.class did the trick here.

Categories