#DataJpaTest loads context so fails on missing beans - spring

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.

Related

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'

Data JPA Test with reactive spring

I want to test the repository layer and I'm using spring webflux. My test class is as follows
#RunWith(SpringRunner.class)
#DataJpaTest
public class DataTester {
#Autowired
private MyRepository repository;
#Test
.....
}
Even though this would work in spring-mvc when using spring-weblux I get the following error.
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.context.ApplicationContextException: Unable to start ReactiveWebApplicationContext due to missing ReactiveWebServerFactory bean.
How to resolve this? If I am to start the whole application context with #SpringBootApplication it works. Any other options without using that?
The reason for this was that in the application.properties the application type was set as reactive.
spring.main.web-application-type=reactive
This tries to auto configure a web server in this case a reactive web server. As #DataJpaTest does not provide a bean for that, this fails. This can be fixed in either two ways.
One is by Adding an application.properties file in the resources directory of the test package and setting the value as,sprig.main-web-application-type=none solves this issue.
Or we can simple pass a property value to the annotation as follows. #DataJpaTest(properties = "spring.main.web-application-type=none")
If you are using Spring Boot 2+ then only #DataJpaTest is enough on test class.
So your test class should be
#DataJpaTest
public class DataTester {
#Autowired
private MyRepository repository;
#Test
.....
}

Configuration of SpringJUnit4ClassRunner Test clashes with SpringBootTest

I have a bunch of tests in my project that are all annotated with #SpringBootTest and therefore load up a SpringBoot context.
Now recently I refactored a Test in which I wanted a smaller scope (it´s about process coverage with camunda) to #RunWith(SpringJUnit4ClassRunner.class).
Since this means that no context is loaded automatically I create some beans "manually" with a static inner class configuration. The entire test looks something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {
ExternalConfiguration.class, MyTest.InternalConfiguration.class
})
public class MyTest{
#Autowired
private SomeBean someInternalBean;
#Configuration
public static class InternalConfiguration{
#Bean
SomeBean someInternalBean() {
return mock(SomeBean .class);
}
}
//Tests
Now, this test runs fine when I run it. BUT when I run any other test ( those still annotated with #SpringBootTest), I get issues with when the ApplicationContext is loaded:
The bean 'someInternalBean', defined in class path resource [.../MyTest$InternalConfiguration.class], could not be registered. A bean with that name has already been defined in file [.../SomeBean.class] and overriding is disabled.
Apparently a bean is created when loading the ApplicationContext because the class is annotated with #Component AND the context loader tries to create another bean from my internal configuration.
I cant allow bean-overriding because my mock beans might overwrite the automatically created beans (which they do, I tried).
How do I circumvent this? I want my SpringJUnit4ClassRunner-tests with their internal configurations to not affect my other #SpringBootTest-tests. I already tried making the configuration beans conditional with #ConditionalOnMissingBean but that did not work.
Turns out those inner configuration classes should not be annotated with #Configuration. Removing the annotation makes it so that the manual bean generation still works and the configuration is no longer picked up by the componentScan.

Initializing Mongo DB for Spring Boot Test

I'm building a Spring Boot application that uses Spring Data Repositories with MongoDB. I'm attempting to create a Spock functional Spec to test my repository but I can't figure out the appropriate way to initialize the Mongo DB in preparation for testing. So far I have tried the following:
Do Nothing - This resulted in the same database being used from test to test with my tests failing after.
Drop the database before testing - This resulted in the indexes being lost and me being unable to test my unique indexes.
Here's what I was doing with dropping the database:
#ContextConfiguration(classes = MyApp, loader = SpringApplicationContextLoader)
#ActiveProfiles('test')
class UserRepositoryTest extends Specification {
#Shared
boolean mongoReset = false
#Autowired
MongoTemplate mongoTemplate
#Autowired
UserRepository userRepository
void setup() {
if (!mongoReset) {
mongoTemplate.getDb().dropDatabase()
mongoReset = true
}
}
}
Ideally I'd like to be able to use something similar to the data.sql method provided with JPA repositories.
We usually recommend to rather use the repository to wipe the database (i.e. calling userRepository.deleteAll()). Dropping the database has the downside of wiping all the indexes that might have been created during context bootstrap time.

Spring JUnit Cannot Run TransactionSynchronizationManager.bindResource, while Normal Spring Env Can

I have a working Spring/Hibernate based web application. Now I need to use Spring JUnit 4 to write an integration test for it.
Here is my test code:
#RunWith(SpringJUnit4ClassRunner.class)
#TransactionConfiguration(transactionManager = "hibernateTransactionManager", defaultRollback = true)
#ContextConfiguration(locations = {"classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext.xml"})
public class TestXXX extends AbstractTransactionalJUnit4SpringContextTests {
#Test
public void testXXXExecute(){...}
}
With this setting of the test environment, I can access all the beans and use the sessionFactory bean to get data from database.
The problem happens with one test, with calls a production code using TransactionSynchronizationManager to implement two-phase commit.
The code looks like this:
TransactionSynchronizationManager.bindResource(sessionFactoryA, new SessionHolder(sessionA));
TransactionSynchronizationManager.bindResource(sessionFactoryB, new SessionHolder(sessionB));
The code performs well in the dev and production environment, where the full Spring Framework is running. During the JUnit run, the exception is:
[junit] java.lang.IllegalStateException: Already value [org.springframework.orm.hibernate3.SessionHolder#6311e359] for key [org.hibernate.impl.SessionFactoryImpl#56d47236] bound to thread [main]
I cannot use 2 lines of #TransactionConfiguration in the test class to define the two transaction managers that corresponds to the two data sources and two sessionFactory objects. I wonder if AbstractTransactionalJUnit4SpringContextTests cannot duplicate the transaction environment of the real Spring Framework.
Without seeing more of your code, it is difficult to tell exactly what is wrong. In instances where I've seen this error in the past, it was because files named in the #ContextConfiguration included each other. For example, you might have file
applicationContext-bean-cfg.xml
that includes
applicationContext-hibernate-cfg.xml, but then have
#ContextConfiguration(locations = {"classpath:/applicationContext-bean-cfg.xml", "classpath:/applicationContext-hibernate-cfg.xml"}).
The other thing to check is that one of the files doesn't already have a transaction manager defined.

Resources