Change flush mode in Spring Boot tests when #DataJpaTest in use? - spring

How do you get Spring tests using #DataJpaTest to use COMMIT flush mode? This is not working for my Spock unit tests:
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest(properties = "spring.jpa.properties.org.hibernate.flushMode=COMMIT")
class MyJpaTestSpec extends Specification {
#Autowired
EntityManager entityManager
def "test flush mode"() {
expect:
// confirming that flushMode is still AUTO even though I configured COMMIT
entityManager.flushMode == FlushModeType.AUTO
}
}
entityManager.getFlushMode() continues to return AUTO.
This is with Spring Boot 2.2.2 with a dependency on spring-test:5.2.2.RELEASE.
I also have a configuration class:
#AutoConfigurationPackage
#SpringBootConfiguration
class MyConfiguration {
}
And src/test/resources/application.yml (Gradle) appears to being read because certain configurations are being picked up (such as the datasource), but the spring.jpa.properties.org.hibernate.flushMode property appears to be ignored from the config file while executing the #DataJpaTest tests.
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:mydb;DB_CLOSE_DELAY=-1
username: sa
password: sa
jpa:
# seems to be ignored for #DataJpaTests
properties:
org.hibernate.flushMode: COMMIT
I've tried hacks like adding a #PostConstruct in MyConfiguration to call setFlushMode() on the entityManager but that doesn't work either. By the time the test is run, it has reverted back to AUTO flush mode. (I'm guessing it reverts back to AUTO for every new session.)

I should have tried this before even posting the question, but it turns out this appears to be a Spring bug that has recently been fixed. I upgraded to Spring Boot v2.2.6 (which uses spring-test v5.2.5). When I posted my question, I was using Spring Boot v2.2.2. The upgrade from v2.2.2 to v2.2.6 has solved the problem. On v2.2.6, spring.jpa.properties.org.hibernate.flushMode is not ignored in my test like it was with v2.2.2.

Related

#DataJpaTest loads context so fails on missing beans

Been at this for a few hours now and tried various things with no avail.
I am trying to write basic #DataJpaTest classes to test my native queries etc.
The issue is the test seems to try load the entire SpringApplication context rather than the "slice" required for the tests.
reflectoring.io - Data JPA Tests
As far as I understand, #DataJpaTest should only loads up the bare minimum beans for Entity Management, Repositories and a few other basic beans.
I am overriding the "default H2 database" it uses (as I have custom native SQL queries) so my test looks like:
#DataJpaTest
#ActiveProfiles("jpa")
#Log4j2
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class BrandServiceJpaDataTest {
#Autowired
private WebApiBrandServiceRepo brandServiceRepo;
#Autowired
private BrandRepo brandRepo;
#Test
#DisplayName("Get Brand Services from REPO")
public void getBrandServices() {
final List<BrandService> all = brandServiceRepo.findAll();
log.info(all);
}
}
Properties are simple, I was trying to use #TestContainers but would rather have this working with a basic local hosted DB:
#============ DATABASE ===========#
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/aspwtest
spring.datasource.username=dev
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.show-sql=true
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = create-drop
When running the test, I can see in the logs the application context being loaded up with and leading to missing bean errors as it seems to be trying to load far too much of the context (i.e. missing RestTemplate/Validation beans etc.).
Description:
Field validator in ms.allstar.wallet.webserver.patching.EntityPatcher required a bean of type 'javax.validation.ValidatorFactory' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'javax.validation.ValidatorFactory' in your configuration.
Is there some form of config I am missing?
I know I could probably go around annotating some beans like #ActiveProfile("jpadatatest") but that feels very hacky and would sort of defeat the purpose of #DataJpaTest existing.

#EnableBatchProcessing breaks other JPA junit tests

I have a microservice where multiple #DataJpaIntegrationTest junit tests are present. As soon as I am adding #EnableBatchProcessing , all junits annotated with #DataJpaIntegrationTest are failing.
Problem seem to be as no transaction is happening.
I am using custom batch configurer to use Map based JobRepository utilizing Resourceless transactionManager.
#DataJpaIntegrationTest looks as below:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#SpringBootTest
#Transactional
#AutoConfigureDataJpa
#AutoConfigureTestDatabase
#AutoConfigureTestEntityManager
#TestPropertySource(properties = {
"spring.cloud.stream.function.definition="
})
public #interface DataJpaIntegrationTest {
}
Let me know if further details are required
I got the root cause of the problem but unaware of the solution.
As soon as I removed custom batch configurer , everything started working fine.
However, #DataJpaTest is still failing with below reason
"eventSource":"org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer","message":"No transaction manager was provided, using a DataSourceTransactionManager"
How can I have the jpa transaction intact while using map based repository.

Triggering Flyway migration before each test

I'm implementing an integration test suite in a Spring Boot project. My project is using Flyway for database migration.
My test class looks similar to this:
#SpringBootTest
#AutoConfigureMockMvc
#Testcontainers
internal class MyIntegrationTest {
#Container
private val postgres = postgresContainer
#Autowired
private lateinit var mockMvc: MockMvc
// ... several test cases
}
Spring Boot runs Flyway migration at application context startup. The problem is that #SpringBootTest starts application context only once, so my DB is initialized only before all tests and not before each test.
I also tried injecting a Flyway field in my test class:
#Autowired
private lateinit var flyway: Flyway
#BeforeEach
fun setup() {
flyway.migrate()
}
but there is No qualifying bean of type 'org.flywaydb.core.Flyway' available.
So, which is the right way to trigger migration before each test?
NOTES:
(PostgreSQL) test container is correctly created and started for each single test.
I'm using this config in application.properties: spring.flyway.enabled=false
Found a solution thinkering with JUnit and Spring Boot configuration.
The reason for No qualifying bean of type 'org.flywaydb.core.Flyway' available was my wrong configuration: spring.flyway.enabled=false. This is disabling also the creation of a Flyway instance, not only the initial migration.
So I removed this property from application.properties, and added this code to my test class (as also suggested by Onome Sotu in his comment):
#Autowired
private lateinit var flyway: Flyway
#BeforeEach
fun setup() {
flyway.clean()
flyway.migrate()
}
I also added #TestInstance(TestInstance.Lifecycle.PER_CLASS) to the test class: this way the container is created and started just once (no need to recreate the database from scratch now that I can clean it before each test). Additionally, this makes tests execution very faster.
NOTE: Actually, with this setup, container creation is executed twice: one at context startup and one on test class creation. Similarly, DB migration is executed for each test case +1 (the +1 happens at context startup). I'd like to find a way to disable unnecessary container creation and database migration)...
Try annotating your tests like this:
#Test
#FlywayTest
public void testFunction(){..}
From Flyway documentation:
Annotation FlywayTest for database unit testing. Use Flyway feature.
clean - execution of flyway task clean
init - execution of flyway task init
migrate - execution of flyway task migrate
https://github.com/flyway/flyway-test-extensions
Also, make sure to include the following dependencies:
testCompile 'org.flywaydb:flyway-core:6.4.1'
testCompile 'org.flywaydb.flyway-test-extensions:flyway-spring-test:6.3.3'

JUnit 4 & Spring Boot - selectively reload context/reload Spring Security configuration before test

I use the SpringJUnit4ClassRunner to run integration tests for a Spring Boot application.
During my search I found out it is possible to reload the app context with #DirtiesContext.
My issue: I only need to reload the security configuration (which depends on a DB entry), while keeping the rest as is (or to be precise: I need to keep the H2 database as is).
How to only reload the security configuration before a JUnit test?
If you need to keep H2 Database as it is, you might consider setting the property spring.jpa.hibernate.ddl-auto to update, because it will create the database if doesn't exists and will keep it the existing if exists. If you have already have a application-test.properties you can create another properties like application-securityTest.properties.
#... Your DB connection info
spring.jpa.hibernate.ddl-auto=update
Then in your test class you need to activate this profile with the annotation #ActiveProfiles and use the #DirtiesContext to reload Spring context:
#ActiveProfiles("securityTest")
#DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
public class SecurityTest { ... }
I solved the problem another way, I modified the implemention so that the security configuration can variably be modified during runtime and doesn't require the DB entry at startup.

SpringBoot Datasource AutoConfiguration Not Working

I have a simple SpringBoot application and I'd like to use AutoConfiguration to configure the Tomcat jdbc pooled data sources.
I am using these Spring dependencies:
// Spring Boot
compile 'org.springframework.boot:spring-boot-starter-web:1.3.5.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-jdbc:1.3.5.RELEASE'
Here are my datasource properties in my application.yml file:
spring:
datasource:
url: jdbc:mysql://my.host/mydb
username: user
password: pwd
driver-class-name: com.mysql.jdbc.Driver
initialSize: 5
I am sure the properties are being loaded because the app is picking up other values.
I define the bean in my config file as:
#Bean(name="myDataSource")
#ConfigurationProperties(prefix="spring.datasource")
public DataSource getDataSource() {
DataSource dataSource = DataSourceBuilder.create().build()
return dataSource
}
And I inject the datasource into my DAO like this:
#Slf4j
#Repository
class MyDAO {
#Autowired
DataSource dataSource
public void getFoo() {
log.info("DB URL: ${dataSource.getUrl()}")
}
}
If I set a breakpoint in the getDataSource() method, the DataSourceBuilder will create an instance of DataSource. However, all the properties of that object like URL, user and password are all null. Also, when I call getFoo(), the dataSource variable is null. I have tried commenting out the bean definition in my AppConfig. The dataSource is still null. Any suggestions?
I looked through the Spring Boot documentation and my Spring book but I didn't see any examples like this. I see examples where I create the DataSource myself. But I was hoping Spring's auto-configuration would tie this stuff together automatically.
Thanks in advance for any help you can provide.
By creating your own bean, you're actually switching off Boot's auto-configuration of a DataSource. You can just delete your getDataSource method and let Boot auto-configure one instead.
Based on Andy's comments I found out that I had two problems. First of all, I needed to include the JPA dependency to the project. I added this line to my build.gradle file:
compile 'org.springframework.boot:spring-boot-starter-data-jpa:1.3.5.RELEASE'
Second, I was creating instances of MyDAO using new(). I fixed this by creating a service class that used #Autowired to inject an instance of MyDAO. Once the DAO became a Spring managed bean, it was able to inject the instance of DataSource from the Tomcat connection pool.

Resources