NoInitialContextException in Spring boot JPA test - spring

I am working on migrating following spring boot test case from spring boot 1 to spring boot 2.
org.springframework 4.3.10 -> 5.3.18
org.springframework.boot 1.5.6 -> 2.6.6
Original Test case :
#RunWith(SpringRunner.class)
#DataJpaTest
#ContextConfiguration (classes = {UserProfileTestContext.class, UserProfileLocatorContext.class})
#SpringBootTest (classes = {UserProfileTestContext.class, UserProfileLocatorContext.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("UserProfileLocatorContext")
public class UserProfileLocatorIntegrationTestIT {
#Autowired
private UserProfileLocator profileLocator;
#Test
public void locate() throws Exception {
InitialContext ctx = new InitialContext();
NamingEnumeration<NameClassPair> list = ctx.list("");
while (list.hasMore()) {
NameClassPair next = list.next();
System.out.println(next.getName() + " -> " + next.getClassName());
}
profileLocator.locate("user1");
}
}
When I ran this test case with new versions, I got an error saying
java.lang.IllegalStateException: Configuration error: found multiple declarations of #BootstrapWith for test class : [#org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper.class), #org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.context.SpringBootTestContextBootstrapper.class)]
I did a web search regarding this and removed the #SpringBootTest tag [1].
After this I am getting a different error when calling the 'locate' method
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
Any idea on what I am missing here?

Related

How to exclude Mongo spring data configuration only for tests

I am doing a simple sample project with Spring boot and data.
#Configuration
public class MongoConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(MongoConfig.class);
#Value("${mongo.uri}")
private String mongoUri;
public #Bean MongoClient mongoClient() {
LOGGER.info(" creating connection with mongodb with uri [{}] ", mongoUri);
return MongoClients.create(mongoUri);
}
}
This works fine and connects to mongo on startup. However, the tests also pick this up in autoscan. What is the best practice to make sure that mongo config gets excluded for tests?
If I add #WebMvcTest to the tests, it works. But not all tests will be mvc tests. I might be testing a utility class.
If I try using profiles, it gives my an error java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes: ...MongoConfig
#SpringBootTest
#ActiveProfiles("test")
class ApplicationTests {
#Test
void contextLoads() {
}
}
Please tell me a repeatable practice as I will be using it for all my tests.
The simplest way of doing it is to exclude your configuration bean when your test profile is active
#Profile("!test")
#Configuration
public class MongoConfig {
...
}
Here you tell to the BeanFactory to not create this bean if the profile test is present
Ref: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html

Integration Testing With #DataJpaTest

I have this test in my project:
#DataJpaTest
#SpringBootTest
#TestPropertySource(locations = "classpath:local-configuration.properties")
public class CanPixaRepositoryTest {
#Autowired
private CanPixaRepository canPixaRepository;
public void setUp() throws Exception {
}
#Test
public void testAll() {
canPixaRepository].getAvui("fqs");
}
}
local-configuration.properties:
spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=
but when I run the test, canPixaRepositoryRepository is null
For Spring boot applications, the simplest approach is using #DataJpaTest with H2 to test your repositories if you are using JPA and Spring Data JPA.
Add h2 dependency into your test scope. Spring Boot will auto-configure it and use it to replace the runtime DataSource in the testing phase.
Annotate your tests with #DataJpaTest. Then the EntityManager, your repositories, and a test-purpose TestEntityManager are available in the Spring Application Context.
Check my example here.
(Spring 5/Spring Boot 2 added Junit 5 support, it does not require the #Runwith or #Extendwith if you are using Junit 5 integration, the DataJpaTest itself is a meta-annotation, before Spring 2.4, to enable Junit5, you have to exclude JUnit 4 from spring-boot-test and add JUnit 5 manually. In the upcoming Spring Boot 2.4, JUnit 5 is the default test runner)
#SpringBootTest annotation is used to setup the entire application context while the #DataJpaTest will setup the application context so that you could test your jpa related code, it will setup a slice of your application context for this specific use case .So there is no need to use the #SpringBootTest annotation with #DataJpaTest, use it like :
#ExtendWith(SpringExtension.class)
#DataJpaTest
#TestPropertySource(locations = "classpath:local-configuration.properties")
public class CanPixaRepositoryTest {
#Autowired
private CanPixaRepository canPixaRepository;
public void setUp() throws Exception {
}
#Test
public void testAll() {
canPixaRepository.getAvui("fqs");
}
}
Since SpringBoot 2.1 you don't have to provide the #ExtendWithannotation to tell Junit to enable spring support as it is already provided with the annotations like #SpringBootTest and #DataJpaTest.

spring embedded kafka with junit5 - No resolvable bootstrap urls given in bootstrap servers

I'm trying to use Embedded kafka for my tests.I'm using spring boot and junit5 as follows
#SpringBootTest
#EmbeddedKafka
public class MyTest {
//Instead of the class rule approach I'm using
EmbeddedKafkaBroker embeddedKafka = new EmbeddedKafkaBroker(1,true,topics);
..
#Test
public void myTestCase() {
....
}
However,my tests fail with the No resolvable bootstrap urls given in bootstrap servers
I'm using a test profile too where on the yml file I've
bootstrap-servers :{spring.embedded.kafka.brokers}
Please help.
#SpringBootTest initializes the test Spring Boot application context before the test class instance is created and member fields are initialized. Because of that the #SpringBootApplication doesn't see the EmbeddedKafkaBroker as the field is initialized later.
Try following a working example from this answer:
#SpringBootTest
#EnableKafka
#EmbeddedKafka(
partitions = 1,
controlledShutdown = false,
brokerProperties = {
"listeners=PLAINTEXT://localhost:3333",
"port=3333"
})
public class KafkaConsumerTest {
#Autowired
KafkaEmbedded kafkaEmbeded;
}

#Value annotation is not working with Infinitest 5.2.0 on STS 3.9.6

JUnit tests are failing when triggered by Infinitest complaining about wrong configuration values.
E.g.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'databaseConfiguration': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.data.mongodb.database' in value "${spring.data.mongodb.database}"
It seems, that application properties are not loaded, although they present in the resources folder and working fine when the tests are running manually.
I am using Spring Tool Suite 3.9.6 with Infinitest 5.2.0
The unit tests are annotated like this
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes = ChargeConsumerApplication.class)
#ActiveProfiles("test")
public class ChargeConsumerApplicationTest {
#Test
public void contextLoads() {
}
#Test
public void applicationContextLoaded() {
}
#Test
public void applicationContextTest() {
ChargeConsumerApplication.main(new String[] {});
}
}
Is it an STS problem or I need some extra configuration for Infinitest itself?

Modify the bean created in main application context during Integration test

In my springboot application I am performing Integration tests using the following class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AccountLoadApplication.class,
loader = SpringApplicationContextLoader.class)
#WebIntegrationTest(randomPort = true)
public class LoaderTest {
AccountLoadApplication.class is a spring boot main class and the actual application has a bean defined like below:
#Bean
public ResourceLoader recapMvsFileResourceLoader() {
return new RemoteFileResourceLoader(remoteHostProperties(), new SFTPRemoteFileService());
}
Also I have a Test Configuration class like below
#Configuration
public class AtddTestConfig {
#Bean
public ResourceLoader mvsFileResourceLoader() {
ResourceLoader recapMvsFileResourceLoader =
new RemoteFileResourceLoader(remoteHostProperties(), new FakeSFTPRemoteFileService());
return recapMvsFileResourceLoader;
}
My Idea is that I want to override the bean created in the main application using the new bean defined in the test Configuration file.
But during integration tests the main application bean is considered instead of the bean defined in the test application context?
Is There any other way to achieve what i am trying to achieve ?
Additional Info:
Here are the beans defined in my Application configuration class
#Bean
public RemoteFileService remoteFileService() {
return new SFTPRemoteFileService();
}
#Bean
public ResourceLoader recapMvsFileResourceLoader() {
return new RemoteFileResourceLoader(remoteHostProperties(), remoteFileService());
}
Here are the beans defined in my Test configuration class
#Bean
#Profile("local")
#Primary
public RemoteFileService remoteFileService() {
return new FakeSFTPRemoteFileService();
}
Still the production bean is only created instead of this primary bean.
Use #Profile annotation to enable testing bean only in test context
Use #Primary annotation on testing bean, so that spring would use test bean instead of production one.
Here is my Github repository with working example using this mechanism.
Maybe when you add your test configuration as parameter for #ContextConfiguration it resolves problem, e.g.
#ContextConfiguration(classes = {AccountLoadApplication.class, AtddTestConfig.class},
loader = SpringApplicationContextLoader.class)
Along with the other changes suggested by #luboskrnac, you have to declare #ActiveProfiles; otherwise, your local profile is simply ignored.
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("local")
#SpringApplicationConfiguration(AccountLoadApplication.class)
#WebIntegrationTest(randomPort = true)
public class LoaderTest { /* ... */ }
Note that the above assumes that your AtddTestConfig class gets picked up via component scanning by your AccountLoadApplication class.

Resources