Embedded MongoDB with Spring not working in test - java

I am trying to create a test class with embedded MongoDB.
I can't start my test because it seems that embedded mongodb has not started.
The exception that I get is the following:
2021-10-18 17:33:17 INFO - [org.mongodb.driver.cluster:76] - Exception in monitor thread while connecting to server localhost:27019
com.mongodb.MongoSocketOpenException: Exception opening socket
at com.mongodb.internal.connection.AsynchronousSocketChannelStream$OpenCompletionHandler.failed(AsynchronousSocketChannelStream.java:272)
at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:129)
at java.base/sun.nio.ch.Invoker.invokeDirect(Invoker.java:158)
at java.base/sun.nio.ch.Invoker.invoke(Invoker.java:186)
at java.base/sun.nio.ch.Invoker.invoke(Invoker.java:298)
at java.base/sun.nio.ch.WindowsAsynchronousSocketChannelImpl$ConnectTask.failed(WindowsAsynchronousSocketChannelImpl.java:308)
at java.base/sun.nio.ch.Iocp$EventHandlerTask.run(Iocp.java:389)
at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.io.IOException: The remote computer refused the network connection.
The dependency for embedded mongo:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
This is my test class
import ...
#ExtendWith(SpringExtension.class)
#SpringBootTest
#ActiveProfiles("test")
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#EnableReactiveMongoRepositories
#ComponentScan(
excludeFilters = {#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = {MongoReactiveDataAutoConfiguration.class}
)}
)
#Slf4j
public class MyServiceTest {
#Autowired
private MyService myService;
#Test
public void saveDataOnMongoTest(){
}
}
This is my configuration of mongo on my yaml under the test profile
data:
mongodb:
uri: mongodb://localhost:27019/myDb
database: myDb
port: 27019 # embedded test purpose
repositories:
type: reactive
jpa:
repositories:
enabled: true
mongodb:
embedded:
version: 4.0.3
features: sync_delay,no_http_interface_arg,only_with_ssl,only_windows_2008_server

Add exclusion annotation to the main class like
#SpringBootTest
#EnableAutoConfiguration(exclude={MongoAutoConfiguration.class})
It may be working.

Found this because I had the same issue.
I fixed it by adding the following to the src/test/resources/application.properties:
spring.data.mongodb.port=0
spring.mongodb.embedded.version=3.6.5
Port -> 0 means use a random port and the version must be set so it will download and start a mongo db.
This is Kotlin code but it's the test that works for me:
#DataMongoTest
#ExtendWith(SpringExtension::class)
class HolmesDataApplicationTests {
#Test
fun contextLoads() {
}
#Test
fun test(#Autowired mongoTemplate: ReactiveMongoTemplate) {
val objectToSave = BasicDBObjectBuilder.start()
.add("key", "value")
.get()
mongoTemplate.save(objectToSave, "collection")
}
}

Related

Running SpringBootTest keeps raising error "Port 8080 already in use" after adding test/resources/application.properties

Because we are creating tests that do not need to access the Postgres database, we are migrating our tests to use H2 storage. We've created a separate application.properties in src/test/resources/application.properties overriding all values from our default src/main/resources/application.properties.
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.liquibase.enabled=false
spring.liquibase.change-log=
security.oidc_domain=123
security.jwt_key=123
api_url=http://localhost:8080
encryption.secret=123
security.debug=false
allowed_hosts=*
We have the following CoreApplication file which boots the Spring App:
#SpringBootApplication
#EnableScheduling
public class CoreApplication implements CommandLineRunner {
public static void main(final String[] args) {
SpringApplication application = new SpringApplication(CoreApplication.class);
application.run(args);
}
public ConfigurableApplicationContext context;
#Override
public void run(final String... args) throws Exception {
System.out.println(args);
context = SpringApplication.run(CoreApplication.class, args);
}
}
And this is our test file:
#SpringBootTest()
class CoreApplicationTests {
#Test()
void contextLoads() {
}
}
When we run this test (via IntelliJ IDEA) we get the following error:
Caused by: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.boot.web.server.PortInUseException: Port 8080 is already in use
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.22.jar:5.3.22]
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.22.jar:5.3.22]
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.22.jar:5.3.22]
at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
Before we added the custom application.properties in test is was working perfectly, but was hitting a connection limit to PostgreSQL because of the amount of tests, so it looks like this is caused by the application.properties override in tests folder.
you can use
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
to get a random available port for your test.
If you need that random port in your test class you can use
#LocalServerPort
private String port;
to get the port into a variable.
Aside that, I would use spring profiles to use a "test" profile src/test/resources/application-test.properties for tests and adding #ActiveProfiles(value = "test") to my test class.

SpringBootTest doesn't import local properties

I am running full app test and would like to read in all of the properties for my app from src/test/resources but when I add the #TestPropertySource and locations, the app still fails to load.
In production, my config is hosted on an external server and I am able to successfully load the config for local testing with this in the boostrap.yml: spring.cloud.config.uri: {pathToDevelopmentConfigServer} However, when I remove that in favor of trying to read config locally, I can't get load the app.
My setup:
Test class:
#SpringBootTest
#EnableConfigurationProperties
#TestPropertySource(locations = {"classpath:MyApp.yaml", "classpath:MyApp-test.yaml"})
#ActiveProfiles(profiles = {"default", "test"})
#ContextConfiguration(classes = {MyAppProperties.class})
public class MyAppTestClass {
#Test
public void myTest(){
assertTrue(true); //dummy test just to have one for now
}
}
Configuration class:
#Configuration
#ConfigurationProperties(prefix = "test-config")
#Validated
public class MyAppProperties {
#NotNull
private String myPropertyValue;
#NotNull
private Map<String, String> myPropertyMap;
// Getters & Setters
}
Config file (MyApp-test.yaml):
test-config:
myPropertyValue: "testValue"
myPropertyMap:
mapKey: "myMapTestValue"
However, when I try to run this, I get this error:
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under '-test' to com.c.taylor.properties.MyAppProperties$$EnhancerBySpringCGLIB$$fd769f1a failed:
Property: test-config.myPropertyValue
Value: null
Reason: must not be null
Property: test-config.myPropertyMap
Value: null
Reason: must not be null
Action:
Update your application's configuration
I've read several docs about how to load properties into a SpringBootTest, but just haven't had any sort of luck.
Thanks in advance.
edit:
src/main/resources/bootstrap.yml file:
---
spring:
application:
name: MyApp
cloud:
config:
uri: {pathToDevelopmentConfigServer}
enabled: false

Spring boot test without database connection

At first I had the following annotation above my test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#AutoConfigureMockMvc
With that configuration it tries to connect to my database, which will give me this error if my database is not running:
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
I would like my test to run without any connection to a database, which is why I tried to change the annotations, so my test class now looks like this:
#RunWith(SpringRunner.class)
#DataJpaTest
#WebMvcTest(CitizenController.class)
#AutoConfigureMockMvc
public class CitizenControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private CitizenRepository citizenRepository;
#MockBean
private WeeklyCareRepository weeklyCareRepository;
#MockBean
private SubCategoryCareRepository subCategoryCareRepository;
#Autowired
private ObjectMapper objectMapper;
private static List<Citizen> mockCitizenList;
private String citizenJson;
However, I am now getting another error:
java.lang.IllegalStateException: Configuration error: found multiple declarations of #BootstrapWith for test class [controllers.CitizenControllerTest]: [#org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.context.SpringBootTestContextBootstrapper), #org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper)]
Is it possible to run my test without a database connection? If so, what am I doing wrong/missing?
You can just mock the method that will connect to database in your repository class in the #Test method.
#SpringBootTest
#AutoConfigureMockMvc
class StoreApplicationTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private CitizenRepository citizenRepository;
#Test
void contextLoads() {
}
#Test
public void test() {
Mockito.when(citizenRepository.getDataFromDB()).thenReturn("Something you'd like to Return");
}
}
After doing that, citizenRepository.getDataFromDB() will not connect to database when it's called.
Update After Your Comment:
Then you can just create "src/test/resources" and copy your application.properties or application.yml from "src/main/resources" to that directory and comment the mysql connection part.
If you don't have "src/test/resources/application.properties", then spring will read "src/main/resources/application.properties" by default and configure the project according to that file, since you have datasource configuration in it, spring will try to connect to the database, if database server is down, you would get the failure.

Why h2 doesn't see table that was just migrated?

I have a Spring Boot web service backed by PostgreSQL 10. Since there were a few rounds of development Flyway is used to apply all necessary changes in DB.
Now I need to cover one of our modules with test and thus I need to mock PostgreSQL and I decided to use H2. The weird part is that when I run test I can see that I have no problems with DB migration, but when I try to use repository I get
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "MY_TABLE" not found; SQL statement:
I've tried to switch H2 to PostgreSQL mode with MODE=PostgreSQL and DATABASE_TO_LOWER=TRUE in connection url, but it didn't help. Also I keep connection open with DB_CLOSE_DELAY=-1. SQL queries in Flyway scripts seems fine, because on any attempt to modify them I can easily get syntax error.
Config properties
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:test;MODE=PostgreSQL;DATABASE_TO_UPPER=FALSE;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1
username: sa
password: sa
Test class
#SpringBootTest(classes = MyConfig.class)
#RunWith(SpringRunner.class)
public class MailServiceTest {
#Autowired
private MyRepo repo;
#Test
public void x() throws Exception {
repo.findAll(); // exception is thrown here
}
}
Config class
#SpringBootConfiguration
#EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
#PropertySource("classpath:custom-application.properties")
#ComponentScan(basePackages = {"com.example.myproj"})
#EnableJpaRepositories(basePackages = "com.example.myproj", entityManagerFactoryRef = "customEntityManagerFactory", transactionManagerRef = "customTransactionManager")
#EntityScan(basePackages = "com.example.myproj")
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableJpaAuditing(auditorAwareRef = "customAuditorAware")
#EnableScheduling
public class MyConfig {
...
}
Any clue how to fix this or at least where to look for solution? Thank you
Just stuck on the same issue this morning.
Solution was in a configuration inherited from AbstractJdbcConfiguration to change quote handling:
#Bean
#Override
public JdbcMappingContext jdbcMappingContext(Optional<NamingStrategy> namingStrategy, JdbcCustomConversions customConversions) {
JdbcMappingContext mappingContext = super.jdbcMappingContext(namingStrategy, customConversions);
mappingContext.setForceQuote(false);
return mappingContext;
}
TL;DR
Using JdbcTemplate to debug the test I found out, that select * from test is working, but select * from "test" doesn't.
It was the key finding to actually solve this issue.
In my case it was clear it is some kind of regression, because it worked before I upgraded Spring version.
So I started to search "quotes jdbc spring template" in google. Third in list was this:
https://spring.io/blog/2020/05/20/migrating-to-spring-data-jdbc-2-0

How to run a integration test in Spring with #SpringBootTest

I am trying to learn integration tests with Spring. So I am following this tutorial:
http://www.lucassaldanha.com/unit-and-integration-tests-in-spring-boot/
I am fase a test Class like this:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class GreetingControllerTest {
#Test
public void helloTest(){
TestRestTemplate restTemplate = new TestRestTemplate();
Hello hello = restTemplate.getForObject("http://localhost:8080/hello", Hello.class);
Assert.assertEquals(hello.getMessage(), "ola!");
}
}
But when I mvn install, I get this error:
I/O error on GET request for "http://localhost:8080/hello": Connection refused; nested exception is java.net.ConnectException: Connection refused
So... What am I doing wrong? What I need to do to make my test work?
Note: If I run mvn spring-boot:run the project works fine and I request the end point using any browser.
That's because of the following property in your test class:
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
As per spring documentation, it binds the application to a random port. So, while sending the request, there's a chance that the app won't be running on port 8080 and hence, you get connection refused error.
If you want to run the app on a particular port, you need to remove webEnvironment property and annotate your class with the following:
#IntegrationTest("server.port=8080")
Another approach is to get the port and add it into the url, below is the snippet to get the port:
#Autowired
Environment environment;
String port = environment.getProperty("local.server.port");
You can autowire the random port value to a field in the test class if you want to:
#LocalServerPort
int port;
but you can autowire the restTemplate and you should be able to use it with relative URI without the need to know the port number:
#Autowired
private TestRestTemplate restTemplate;
#Test
public void helloTest(){
Hello hello = restTemplate.getForObject("/hello", Hello.class);
Assert.assertEquals(hello.getMessage(), "ola!");
}

Categories

Resources