Kotlin integration Tests with Spring Boot - spring

What I have: I'm developing a microservice, using Spring Boot with Web and MongoDB as storage. For CI integration tests I use test containers. I have two integrations tests with SpringBootTest annotations, which use TestConfig class. TestConfig class provides setup MongoDB test container with fixed exposed ports.
My problem: When I run my tests one at a time then they succeed. But when I run my tests at the same time then they failed.
MongoContainerConfig.kt
#TestConfiguration
class MongoContainerConfig {
var mongoContainer: GenericContainer<Nothing>
constructor() {
mongoContainer = FixedHostPortGenericContainer<Nothing>("mongo")
.withFixedExposedPort(27018,27017)
mongoContainer.start()
}
#PreDestroy
fun close() {
mongoContainer.stop()
}
}
First test
#SpringBootTest(
classes = arrayOf(MongoContainerConfig::class, AssertUtilsConfig::class),
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
class CardControllerTest {
#Test
fun test() {}
}
Second test
#SpringBootTest(classes = arrayOf(MongoContainerConfig::class, AssertUtilsConfig::class))
class PositiveTest {
#Test
fun test() {}
}
Error msg
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mongoContainerConfig': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.lang.card.engcard.config.MongoContainerConfig$$EnhancerBySpringCGLIB$$e58ffeee]: Constructor threw exception; nested exception is org.testcontainers.containers.ContainerLaunchException: Container startup failed
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1320)
a
This project you can see on github with CI
https://github.com/GSkoba/eng-card/runs/576320155?check_suite_focus=true
It's very funny because tests work if rewrite them to java

Hah, it not easy)
https://docs.spring.io/spring/docs/5.2.5.RELEASE/spring-framework-reference/testing.html#testcontext-ctx-management-caching
Tests have different context and its why MongoContainerConfig call twice.
Fix - add webEnv as in CardControllerTest.kt
#SpringBootTest(classes = arrayOf(MongoContainerConfig::class, AssertUtilsConfig::class),
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class PositiveTest
Proof https://github.com/GSkoba/eng-card/actions/runs/78094215

Related

Spring Boot 3.0 upgrade issue, cannot invoke GenericConversionService.addConverter(Converter) because "cs" is null

I'm upgrade the spring boot to 3.0.0-M4 and I have this test case which works fine in Spring 2.7 but not in 3.0.
#ExtendWith(SpringExtension.class)
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.NONE,
classes = {
PubSubBindingsTestConfiguration.class,
BinderFactoryAutoConfiguration.class,
PubSubBinderConfiguration.class,
PubSubExtendedBindingProperties.class,
BindingServiceConfiguration.class
},
properties = {
"logging.level.org.springframework.boot.autoconfigure=DEBUG",
"spring.cloud.stream.pollable-source=customIn",
"spring.cloud.stream.gcp.pubsub.bindings.input.consumer.ack-mode=AUTO_ACK",
"spring.cloud.stream.gcp.pubsub.bindings.input.consumer.auto-create-resources=true",
"spring.cloud.stream.gcp.pubsub.default.consumer.auto-create-resources=false"
})
class PubSubExtendedBindingsPropertiesTests {
#Autowired private ConfigurableApplicationContext context;
#Test
void testExtendedPropertiesOverrideDefaults() {
BinderFactory binderFactory = this.context.getBeanFactory().getBean(BinderFactory.class);
}
}
The test case failed with the following stacktrace:
21:11:36.182 [main] ERROR o.s.boot.SpringApplication - Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'messageConverterConfigurer' defined in org.springframework.cloud.stream.config.BinderFactoryAutoConfiguration: Unsatisfied dependency expressed through method 'messageConverterConfigurer' parameter 0: Error creating bean with name 'spring.cloud.stream-org.springframework.cloud.stream.config.BindingServiceProperties': Cannot invoke "org.springframework.core.convert.support.GenericConversionService.addConverter(org.springframework.core.convert.converter.Converter)" because "cs" is null
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:774)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:524)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1323)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:566)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:526)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)

Unable to call Mapper.xml file by using junit testing for the application developed using Mybatis+Springboot

I'm very new to junit testing. How to write junit test real database call from mybatis.xml file.
Please find the below code.
#RunWith(SpringRunner.class)
//#MybatisTest
#SpringBootTest
public class HotelMapperTest {
#Autowired
private HotelMapper hotelMapper;
#Test
public void selectByCityIdTest() {
Hotel hotel = hotelMapper.selectByCityId(1);
assertThat(hotel.getName()).isEqualTo("Conrad Treasury Place");
assertThat(hotel.getAddress()).isEqualTo("William & George Streets");
assertThat(hotel.getZip()).isEqualTo("4001");
}
when i run the junit testing i'm getting below exception:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):
Herer my question is how we'll test the real database, When enable the #MybatisTest it's looking for datasource, already we specified all properties in applicaiton.properties. In this time i'm getting below exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource':
You can config mybatis mapper location in spring configuration file (such as application.yml).
mybatis configuration:
mybatis:
mapper-locations:
- classpath*:mapper/*.xml

how to override Spring Boot autowired component during testing

I'm trying to write tests for a Spring Boot batch application.
I have an interface "WsaaClient" and two implementations, I need to use one of them for normal execution and the other for testing purposes.
In the project, I have FCEClient class that has an autowired field "LoginManager", which has an autowired field "WsaaClient".
#Component
#Profile("!dev")
public class FCEClient implements IFCEClient {
#Autowired
LoginManager loginManager;
#Component
public class LoginManager {
#Autowired
WsaaClient client;
#Component
public class AfipWsaaClientSpring extends AfipWsaaClient {
AfipWsaaClient is in a non-spring maven dependency. It implements WsaaClient.
Running the Spring Batch application works well and AfipWsaaClientSpring is picked.
Now I want to write a test and need to use a dummy implementation for WsaaClient.
So I put under src/test/java this class:
#Component
public class TestWsaaClientSpring implements WsaaClient {
And this test:
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration
public class FceBatchApplicationTests {
private JobLauncherTestUtils jobLauncherTestUtils;
#Test
public void testJob() throws Exception {
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
Assert.assertEquals("COMPLETED", jobExecution.getExitStatus().getExitCode());
}
}
Running it from JUnit Launcher on Eclipse throws:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'afipWsaaClientSpring' defined in file [/home/guish/vmshare/eclipsews/ec/ec-batch/target/classes/com/mycompany/AfipWsaaClientSpring.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.mycompany.AfipWsaaClientSpring]: Constructor threw exception; nested exception is java.io.FileNotFoundException: ./wsaa_client.properties (No such file or directory)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1303) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1197) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
The FileNotFoundException is not relevant, the file is not present because of running as a test and Spring Boot should not pick the AfipWsaaClientSpring implementation.
How can I override the Autowired option in my test code and choose TestWsaaClientSpring instead?
And just in case, how can I prevent Spring Boot from instantiating the AfipWsaaClientSpring when running as a test?
Annotation #SpringBootTest has 'properties' attribute (https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/SpringBootTest.html).
so, you can specify spring profile like this,
#SpringBootTest(properties = {"spring.profiles.active=test"}, classes=MyConfiguration.class)
As mentioned by Charles Lee you could provide the active profile for SpringBootTest. Also you could do this with the annotation #ActiveProfile("theprofile") on your FceBatchApplicationTests class.

DI in tests without using spring boot (#SpringBootTest)

Switching from spring boot back to "normal" spring because the app only uses some jdbc code to "upsert" into a postgresql database.
1)
tried annotating the test class with:
#RunWith(SpringJUnit4ClassRunner.class)
public class DBIntegration {
results in:
java.lang.IllegalStateException: Failed to load ApplicationContext
2)
tried annotating the class with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {})
public class DBIntegration {
[main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [de.mydomain.myproject.DBIntegration]: no resource found for suffixes {-context.xml}.
No exceptions, but java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=insertDataFrom_sometest],
3) tried annotating the class with:
#Component
public class DBIntegration {
dependency injection does not work in this case, the expected service
(to be injected) throws a nullpointerexception

Usage of Spring #ConfigurationProperties gives nullpointer when running tests, JHipster app

I have implemented a simple file upload routine using Spring Boot ConfigurationProperties annotation so I can load the directory from my YAML configuration file:
Service:
#Service
public class FileSystemStorageService implements StorageService {
private final Logger log = LoggerFactory.getLogger(FileSystemStorageService.class);
private final Path pictureLocation;
#Autowired
public FileSystemStorageService(StorageProperties storageProperties) {
pictureLocation = Paths.get(storageProperties.getUpload());
}
And the StorageProperties:
#Component
#ConfigurationProperties("nutrilife.meals")
public class StorageProperties {
private String upload;
public String getUpload() {
return upload;
}
public void setUpload(String upload) {
this.upload= upload;
}
}
In the yaml file I have:
nutrilife:
meals:
upload: /$full_path_to_my_upload_dir
This works perfectly in normal Spring Boot runtime but the problem starts when I try to run my integration tests, it throws the error:
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fileSystemStorageService' defined in file [/home/mmaia/git/nutrilife/build/classes/main/com/getnutrilife/service/upload/FileSystemStorageService.class]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.getnutrilife.service.upload.FileSystemStorageService]: Constructor threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:279)
...
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.getnutrilife.service.upload.FileSystemStorageService]: Constructor threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:154)
So basically during tests the YAML file configuration looks like it's not being properly picked up. I am new to Spring. How can I fix it?
try adding the #value annotation :
#Value("upload")
private String upload;
The above answer is valid for application.properties config.
it also works with yaml as well.
You may find your correct correct yaml config here :
24.6 Using YAML instead of Properties

Resources