Spring Boot with datasource when testing - java

I am using Spring Boot application and the auto cofiguration is enabled. The main Application file is marked as #EnableAutoConfiguration. The datasource is lookedup from JNDI is configured using java config and the class which create the datasource is marked as #Configuration.
I have a test class as below.
#RunWith( SpringJUnit4ClassRunner.class )
#WebAppConfiguration
#ContextConfiguration( classes = Application.class )
public class TestSomeBusiness {}
The issue is when I run the test case, the datasource jndi lookup happens, which fails because the test case is not running inside a server environment. As far as I know the classes in classpath marked with #Configuration are executed and that the reason the datasource lookup is being called.
The work around for now I have found is instead of JNDI lookup create the datasource using DriverManagerDataSource, so that even if its not a server environment the datasource lookup won't fail.
My questions are:
1) How do we generally deal with datasource (when looking up from JNDI) in
spring boot application for testing ?
2) Is there a way to exclude the datasource configuration class from being called when executing test case ?
3) Should I create an embedded server so that the JNDI lookup can be done when executing test case ?

2) Is there a way to exclude the datasource configuration class from being called when executing test case ?
You can add a application.properties config file into your src/test/resources and spring boot would pick those configurations in test environments. I suppose, you have application.properties in your src/main/resources like this:
spring.datasource.jndi-name=some_jndi
This JNDI resource will be used in your production environment. For your test environment you can use a, say MySQL database, by adding these configurations into your test application.properties:
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
3) Should I create an embedded server so that the JNDI lookup can be
done when executing test case ?
As i said, you can totally bypass the fact that you're using JNDI for production by adding test specific configurations.
1) How do we generally deal with datasource (when looking up from
JNDI) in spring boot application for testing ?
You can mock JNDI resources using facilities available in org.springframework.mock.jndi package. For example by using SimpleNamingContextBuilder you can:
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.bind("jndi_name", dataSource);
builder.activate();
The other option is, of course, using Non JNDI resources in test environments.

Related

How to load the properties based on the environment defined in application.properties in Spring application. NOT with Springboot

I'm new to spring and I'm studying it. And stumbled upon the #Profile annotation.
I want to write a simple project with Spring (not Springboot) to learn how to load properties based on the environment using #profile annotation. Almost everywhere, the examples (Ex1, Ex2) I see only with the Springboot. I'm wondering whether we cannot write a Spring application that can dynamically load the properties based on the environment (dev, prod).
Some examples ( Ex3, Ex4, Ex5) show with the #Profile but those have hardcoded the bean details for each environment like below. Is this how we have to write the property loading?
#Profile("dev")
#Bean
public String devDBCcnnection() {
System.out.println(dbConfiguration.getUrl());
return "DB Connection for Dev";
}
#Profile("test")
#Bean
public String devTestCcnnection() {
System.out.println(dbConfiguration.getDriverClassName());
return "DB Connection for Test";
}
#Profile("prod")
#Bean
public String devProdCcnnection() {
System.out.println("DB Connection for Prod");
return "DB Connection for Prod";
}
It has to write a bean for each profile like in the above example?
Can someone tell me using #Profiles, can't dynamically load the property values like in Spring applications?
Appreciate it if you can give the samples with Spring 5
Almost everywhere, the examples (Ex1, Ex2) I see only with the
Springboot. I'm wondering whether we cannot write a Spring application
that can dynamically load the properties based on the environment
(dev, prod).
Spring boot uses the spring context. The spring context allows you to use profiles. Therefore no problem using profiles with simple Spring project (non spring-boot).
There are many ways that you can use Profiles.
One of them is the example that you gave with specific beans that have #Profile and get registered in spring for a specific profile.
Another one, more commonly used in enteprise applications is to ship a jar application with multiple application.yaml files. So for example you ship your application, containing dev-application.yaml and qa-application.yaml. You can then start your application selecting a specific profile to be active. Then that specific application.yaml will be used when the application starts up to build the spring context. So the aplication will be started with qa-application.yaml and will have a connection to the QA database.
But be careful the default application.yaml will also be loaded. The specific application.yaml for example qa-application.yaml will be loaded on top of default application.yaml.
The following article contains very good information about spring profiles
spring profiles article
Considering my example here, I quotte something relevant from that article.
The Default Profile The default profile is always active. Spring Boot
loads all properties in application.yml into the default profile. We
could rename the configuration file to application-default.yml and it
would work the same.
Other profiles will always be evaluated on top of the default profile.
This means that if a property is defined in the default profile, but
not in the qa profile, the property value will be populated from the
default profile. This is very handy for defining default values that
are valid across all profiles.
In order to activate a specific profile
For non spring-boot projects here is a very good answer spring active profile
For spring-boot projects you can
Use a system variable to start your jar file
java -Dspring.profiles.active=qa -jar myApp.jar
Use an environment property to start your jar file
export SPRING_PROFILES_ACTIVE=qa
java -jar myApp.jar

Lazy load #EnableCaching class in spring

I want my spring boot server to be up even if my redis cache isnt up.
I am using #EnableCachingannotation , but my server startup fails if redis is down giving me BeanCreationException. Because BeanCreationException cannot be handled , the only option I have is to lazy load my #EnableCaching class.
I tried annotating that class as under:
#Configuration
#EnableCaching
#Lazy
#Profile("dev")
public class RedisCache extends CachingConfigurerSupport {}
But still this bean is getting loaded at server startup and startup therefore fails.
How do I lazy load the above mentioned class
As far as I know Spring configuration is alwasy loaded on application startup, because that is when the ApplicationContext is created.
In order to do what you want you'd either have to create some sort of custom implementation of ApplicationContext (although I honestly cannot think of how it should work regarding the dependency resolution for dependency injection) or create custom wrapper for caching which would not try to establish connection to Redis until the cache is used.
It may also be possible to configure Spring Boot to skip this particular #Configuration class (Using Boot configuration classes) and then to manually create AnnotationConfigApplicationContext and then retrieve Redis connection Beans from this context manually rather than autowiring them.

Spring & Maven - how to provide custom properties to Spring application

I have a Spring application which runs in a web container (Tomcat). This Spring application uses a properties file to find the database JDBC location:
#Configuration
#PropertySource("classpath:app.properties")
public class MyApplication {
}
In app.properties, I have:
database.dataSource.url=jdbc:postgresql://localhost:5432/app
It's now easy to get the value at runtime:
#Component
class DatabaseConfiguration {
#Value("${database.dataSource.url}")
private String URL;
}
So far, so good. Now I am using the cargo-maven2-plugin plugin to deploy the WAR during an integration test. Before the WAR is deployed, an ad-hoc PostgreSQL database is deployed into a Docker container via the docker-maven-plugin plugin. This instance runs on a custom, dynamic port instead of the usual 5432. This port is filled in into the ${database.port} property by the docker-maven-plugin plugin.
This means that I need to somehow alter app.properties on the fly to fill in this port. This seems hacky, so maybe there is a way to provide/override the port via the cargo-maven2-plugin to my Spring application, so I could use that one instead of the one in app.properties?
What is a 'clean' way to achieve this?
Use #TestPropertySource to use a different properties file for testing. https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/TestPropertySource.html

Setting up JNDI connection with Spring Boot

I'm trying to set up a JNDI database connection for my WAR file which will be deployed on WebLogic 12c. This is what I have:
#Bean
public DataSource dataSource() throws DataSourceLookupFailureException {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource("jndiName");
}
In the application.properties file:
spring.datasource.jndi-name=jndiName
This is pieced together from two different sections of the Spring Boot guide:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html#howto-configure-a-datasource
I am confused, why is jndiName stated twice? Am I following correctly - is this the correct way to set up?
I am of the impression that Spring Boot will either automatically retrieve the JNDI name from application.properties, so that I do not need to hardcode the JNDI name into getDataSource(), or if I hardcode the JNDI name in then I wouldn't need to include that property in application.properties in the first place.
From my interpretation, the two sections of the guide covering how to set up a JNDI connection appear to contradict each other. What am I getting wrong? What exactly is the correct way to set this up?
Remove your custom bean and use application.properties but in local environment if you want to run embedded container this approach won't work
To use H2 or any other database in embedded container define profile in main method and then configure your data source. This way it works both in local and production

Spring4 JUnit tests : Load SQL to a H2 db

I'm trying to write tests for a Spring Boot (Spring 4) Application.
My Junit test class is configured like this to allow autowired.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = SpringApp.class)
public class MyServiceTest {
...
My src/main/resources/application.properties is like this
spring.jpa.database=POSTGRESQL
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=update
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost/mydb
spring.datasource.username=BNF0016779
spring.datasource.password=
In test context, src/test/resources/application.properties is just empty.
In can query the db as usual, creating objects...
But I'd like to create a data init sql.
To begin with a strange behavior, It seems that Spring loads any "schema.sql" in classpath.
Something like the following is not required ?
//This is not required to execute schema.sql
#Configuration
public class DatabaseTestConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.build();
}
}
Then, I can't create any Table from this SQL.
Always receive org.h2.jdbc.JdbcSQLException: Table "MY_TABLE" already exists; SQL statement:
H2 is supposed to be a in-memory DB, no keeping data between two startup !
Why do I receive these errors ?
Any ideas ?
Thanks
Spring Boot will in fact execute a file named schema.sql in the root of the classpath by default. Furthermore, Spring Boot will also automatically create an embedded database for your application unless you instruct it otherwise. Consult the "Initialize a database using Spring JDBC" section of the Spring Boot reference manual for details.
H2 is supposed to be a in-memory DB, no keeping data between two
startup !
Yes and no.
If Spring Boot creates an embedded H2 database for you, yes it will be in-memory.
However, the database is actually a bean in the ApplicationContext (just like any other Spring-managed component). Thus it lives as long as the ApplicationContext lives, and the Spring TestContext Framework caches contexts between tests: that's one of its main features. In other words, the embedded database will not be recreated between tests (unless you annotate your test classes or test methods with #DirtiesContext). Consult the Context caching section of the Spring Framework reference manual for details.
Regards,
Sam (author of the Spring TestContext Framework)
H2 can be in memory. But I'm assuming the default DataSource it uses is not.
You can set the DataSourceFactory on the EmbeddedDatabaseBuilder to generate a DataSource that connects with a url such as jdbc:h2:mem:test.

Categories

Resources