How do I configure my Spring Boot application so that when I run unit tests it will use in-memory database such as H2/HSQL but when I run Spring Boot application it will use production database [Postgre/MySQL] ?
Spring profiles can be used for this. This would be a specific way:
Have environment specific properties files:
application.properties:
spring.profiles.active: dev
application-dev.properties
spring.jpa.database: MYSQL
spring.jpa.hibernate.ddl-auto: update
spring.datasource.url: jdbc:mysql://localhost:3306/dbname
spring.datasource.username: username
spring.datasource.password: password
application-test.properties
spring.jpa.database: HSQL
Have both MySQL and H2 drivers in pom.xml, like this:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
</dependency>
Last but not the least, annotate Test classes with #ActiveProfiles("test").
Another approach is to add the annotation #AutoConfigureTestDatabase to you test class.
My tests usually look like this:
#RunWith(SpringRunner.class)
#DataJpaTest
#AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
public class MyRepositoryTest {
#Autowired
MyRepository repository;
#Test
public void test() throws Exception {
// Tests...
}
}
Note that the embedded database dependency needs to be added in the pom.xml file.
For embedded database this annotation is not necessary it will work even if only the dependency is added in pom file.
With #SpringBootTest magic, you just need to do following two changes.
Add 'h2' test dependency in pom.xml
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
Use #AutoConfigureTestDatabase
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MySpringBootApplication.class)
#AutoConfigureTestDatabase
public class SpringBootTest{
#Autowired
private RequestRepository requestRepository;
}
Now all the spring jpa bean/repositories used in test will use h2 as backing database.
2019-04-26 13:13:34.198 INFO 28627 --- [ main]
beddedDataSourceBeanFactoryPostProcessor : Replacing 'dataSource'
DataSource bean with embedded version
2019-04-26 13:13:34.199 INFO 28627 --- [ main]
o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition
for bean 'dataSource'
2019-04-26 13:13:36.194 INFO 28627 --- [ main]
o.s.j.d.e.EmbeddedDatabaseFactory : Starting embedded database:
url='jdbc:h2:mem:2784768e-f053-4bb3-ab88-edda34956893;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false',
username='sa'
Note: I still have 'spring-jpa' properties defined in 'application.properties' and I don't use any profiles. #AutoConfigureTestDatabase will override existing jpa configurations with test defaults AutoConfigureTestDatabase.Replace.
Simplest solution:
1) in src/main/resources have application.properties (production config):
spring.datasource.url=jdbc:mysql://localhost:3306/somedb
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.jpa.database-platform = org.hibernate.dialect.MySQL5Dialect
and application-test.properties with HSQL config like:
spring.jpa.hibernate.ddl-auto = create-drop
spring.jpa.database = HSQL
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.HSQLDialect
spring.datasource.driverClassName = org.hsqldb.jdbcDriver
spring.datasource.url= jdbc:hsqldb:mem:scratchdb
spring.datasource.username = sa
spring.datasource.password =
2) Add HSQL dependency in pom.xml if you don't have it already.
3) Annotate your test class with #ActiveProfiles("test").
Worked like charm in my case.
#Sanjay has one way to put it but I find it confusing. You could just as well have only a production profile that you enable when you're in production, something like:
spring.jpa.hibernate.ddl-auto: update
spring.datasource.url: jdbc:mysql://localhost:3306/dbname
spring.datasource.username: username
spring.datasource.password: password
And don't specify anything else. If you add an embedded database in test scope, it will be available in your tests. If you run your tests with the default profile (no customization whatsoever), it won't find any database information (since these are stored in the production profile). In that case, it will try to find an embedded database and start it for you. If you need more customization for some reason, you can have a application-test.properties for those (you'll need to add ActiveProfiles("test") to your test(s).
Simple solution if building with maven: just place an application.properties file under src/test/resources and edit as appropriate for testing.
The Spring (Boot) Profile mechanism is a pretty powerful tool that, in scope, goes way beyond "swapping settings between test time and run time". Although, clearly, as demonstrated, it can do that also :)
This solution enables common settings for develop and test. Is based on this solution:
Override default Spring-Boot application.properties settings in Junit Test
application.properties in src/main/resources/application.properties
#common settings for DEVELOPMENT and TEST:
......
......
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url=jdbc:postgresql://localhost:5432/databasename
spring.datasource.username=postgres
spring.datasource.password=somepassword
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = none
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
test.properties (src/main/resources/application.properties) which overrides and adds properties in application.properties:
spring.datasource.url=jdbc:h2:mem:testdb;MODE=PostgreSQL
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=false
settings in pom.xml for H2 and Postgre databases
<!-- h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<!-- postgress -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
In test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#TestPropertySource(locations = "classpath:test.properties")
public class ModelTest {
}
I have a multi-module Gradle SpringBootApplication with below Modules
employeemanagerApp - Where my SpringApplication main class
employeemanagerIntTests - Where i have my cucumber tests
My requirement was to use MySQL DB when the application boots up and H2 during my Cucumber Integration testing
Solution: In my employeemanagerApp module, src/main/resources i placed the application.properties with below content
#My SQL Configuration
spring.datasource.url=jdbc:mysql://localhost:3306/employeemanager
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
And in the Integration Test Module (employeemanagerIntTests) src/test/resources I placed the application.properties with below content
#H2 In-Memory DB Configuration
spring.datasource.url=jdbc:h2://mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.format_sql=true
And in my Step Definition Class i added only these annotations
#CucumberContextConfiguration
#SpringBootTest(classes = SpringBootApplicationMainClass.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
In the build.gradle file i added H2 dependency
testImplementation 'com.h2database:h2:1.4.200'
So when I ran my tests, H2 was active and all tests with Create, Update, Read and Delete were successful
Related
I am using spring boot 1.5.15.RELEASE.
In my pom.xml I have:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Which as I understand by default favours the tomcat embedded server (which I'm happy with). Indeed this seems the be the case when I run the application in "production mode" I see:
Tomcat initialized with port(s): 8080
However, when running tests like this:
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "spring.profiles.active=test", webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class EmbeddedTomcatServerTest {
...test methods
}
I see:
Jetty started on port(s) 63742 (http/1.1)
A quick mvn dependency:tree shows:
[INFO] +- com.github.tomakehurst:wiremock:jar:2.18.0:test
[INFO] | +- org.eclipse.jetty:jetty-server:jar:9.4.11.v20180605:test
I am using wiremock in tests, my guess is that its presence on the test class path is coercing the spring boot auto configuration to favour Jetty instead of Tomcat in tests and the lack of the wiremock dependency on the runtime classpath reverts back to Tomcat.
I'd like both my tests and production code to use tomcat - is there a way I can ask spring to favour Tomcat even if Jetty is on the classpath.
Provide your own EmbeddedServletContainerFactory bean. For example, to always use Tomcat, use the following bean configuration:
#Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory();
}
When I try to run the spring boot application I am getting the below error
starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2018-05-03 10:50:09.457 ERROR 4909 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
*************************** APPLICATION FAILED TO START
***************************
Description:
Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified and no embedded datasource could be auto-configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following: If you want an embedded database (H2, HSQL or Derby), please put it on the classpath. If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
The above answers are correct in case you want to use a non-embedded data source. In case you want to use an embedded data source, you will only need to add the following dependency to your pom.xml:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
If you want to use the embedded data source in your tests, then change the scope to test instead of runtime.
If you want auto-configuration of datasource, then place the configuration details in application.properties file. Something like this(configuration for MySQL)
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
I have a project A that use SPRING BOOT and it have this ConfigurationClass for this
package it.blabla.common.couponing.configuration;
#Configuration
#ComponentScan(basePackages = { "it.***", "it.**" })
#EnableAutoConfiguration
#PropertySource("classpath:couponing-${application.environment}.properties")
public class CouponingConfiguration {
#Autowired
private Environment env;
..
I have Spring Project B without Spring-Boot that IMPORT and USE Project A.
How can i import Spring-Boot configuration in project B?
For example, for others project that using spring xml file I use this instruction in ext-spring.xml project B
<import resource="classpath:META-INF/projectA-spring.xml"/>
I try import spring Boot Configuration using
<bean class="it.blabla.common.couponing.configuration.CouponingConfiguration "></bean>
but receive this error
LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: Root WebApplicationContext
What is the best way for do that? Is possible? Can I use springBoot Project in a non-spring-boot project ?
It was e dependency version problem
I imported configuration:
<bean class="it.blabla.common.couponing.configuration.CouponingDatabaseConfiguration"></bean>
and change this versions
<spring.version>4.3.2.RELEASE</spring.version>
<spring.data.version>1.10.2.RELEASE</spring.data.version>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring.data.version}</version>
</dependency>
Now it works and I haven't console errors
Could you please help me set up connection to the embedded Derby database in Spring Boot application?
I searched the web but can only find solutions for server-type Derby, not for embedded Derby.
spring.jpa.database = ?
spring.jpa.hibernate.ddl-auto = create-drop
Derby as an in-memory database
If you want to Configure in-memory Derby database with spring boot, the minimal thing you need is to mention the runtime dependency (along with spring-boot-starter-data-jpa) as
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<scope>runtime</scope>
</dependency>
This bare minimum configuration should work for any embedded datasource in Spring Boot. But as of current Spring Boot version (2.0.4.RELEASE) this lead to an error only for Derby
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing
DDL via JDBC Statement
Caused by: java.sql.SQLSyntaxErrorException: Schema 'SA' does not exist
This happens because of default configuration ofspring.jpa.hibernate.ddl-auto=create-drop see this spring-boot issue for details.
So you need to override that property in your application.properties as
spring.jpa.hibernate.ddl-auto=update
Derby as a persistent database
If you want to use Derby as your persistent database. Add these application properties
spring.datasource.url=jdbc:derby:mydb;create=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.DerbyTenSevenDialect
spring.jpa.hibernate.ddl-auto=update
You don't need connection properties if you are using Spring Boot. Just add these to your POM:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
</dependency>
Then add the usual controller, service, and repository classes.
I'm trying to use Spring Data JPA in a Spring Boot project in this tutorial. These are my pom.xml dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
and application.properties
# DataSource settings: set here configurations for the database connection
spring.datasource.url = jdbc:mysql://localhost/dao
spring.datasource.username = root
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.password =
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate settings are prefixed with spring.jpa.hibernate.*
spring.jpa.hibernate.ddl-auto = update
spring.jpa.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming_strategy = org.hibernate.cfg.ImprovedNamingStrategy
and I get this error:
Caused by: org.springframework.beans.factory.BeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath.
at org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.getDriverClassName(DataSourceProperties.java:137)
at org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$NonEmbeddedConfiguration.dataSource(DataSourceAutoConfiguration.java:117)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiat e(SimpleInstantiationStrategy.java:162)
... 40 more
Is there a problem in datasource configuration?
The error is thrown in DataSourceProperties.getDriverClassName() method. Find below the source code of the same from spring distribution:
if (!StringUtils.hasText(driverClassName)) {
throw new BeanCreationException(
"Cannot determine embedded database driver class for database type "
+ this.embeddedDatabaseConnection
+ ". If you want an embedded "
+ "database please put a supported one on the classpath.");
}
Spring throws this error when spring.datasource.driverClassName property is empty. So to fix this error, make sure that the application.properties is in the classpath.
I ran into that same error and my problem was that the application.properties was not packaged in the
jar and I wasn't providing it on startup.
If you are starting up using java -jar you-jar-name.jar make sure that the application.properties is available. Per the docs:
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:
A /config subdir of the current directory.
The current directory
A classpath /config package
The classpath root
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-application-property-files