I'm using Spring Boot v2.6.2 and Java v17 and trying to test my MongoConnection without having a MongoDBService running because it should be tested with embedded in-memory MongoDB on the build machine, no need to set up an extra MongoDB service there. For sure on the productive system, it should use a full MongoDB.
I try to get this easy example running:
@DataMongoTest
public class MongoTest
{
@Autowired
private UserRepository userRepository;
@AfterEach
void cleanUpDatabase()
{
this.userRepository.deleteAll();
}
@Test
void bootstrapTestDataWithMongoTemplate() {
final var restaurant = new User( "123", "ABC", "DEF" );
this.userRepository.insert( restaurant );
final var found = this.userRepository.findByFirstName( "ABC" );
System.out.println( found );
}
}
UserRepository
is an interface that extends MongoRepository
. User
itself is just a DBEntity
with @Document
annotation. If I let the SpringBoot application run, not in test mode, everything works fine, because MongoDB is running at the specified location. But for the test, I want to let it run as an in-memory DB.
But Springboot wants to connect for the test.
2022-01-20 08:31:57.489 INFO 3976 --- [ main] org.mongodb.driver.cluster : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms'}
2022-01-20 08:31:57.521 INFO 3976 --- [localhost:27017] org.mongodb.driver.cluster : Exception in monitor thread while connecting to server localhost:27017
com.mongodb.MongoSocketOpenException: Exception opening socket
at com.mongodb.internal.connection.SocketStream.open(SocketStream.java:70) ~[mongodb-driver-core-4.4.0.jar:na]
at com.mongodb.internal.connection.InternalStreamConnection.open(InternalStreamConnection.java:180) ~[mongodb-driver-core-4.4.0.jar:na]
at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.lookupServerDescription(DefaultServerMonitor.java:188) ~[mongodb-driver-core-4.4.0.jar:na]
at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:152) ~[mongodb-driver-core-4.4.0.jar:na]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Caused by: java.net.ConnectException: Connection refused: no further information
at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:597) ~[na:na]
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) ~[na:na]
at java.base/java.net.Socket.connect(Socket.java:633) ~[na:na]
at com.mongodb.internal.connection.SocketStreamHelper.initialize(SocketStreamHelper.java:107) ~[mongodb-driver-core-4.4.0.jar:na]
at com.mongodb.internal.connection.SocketStream.initializeSocket(SocketStream.java:79) ~[mongodb-driver-core-4.4.0.jar:na]
at com.mongodb.internal.connection.SocketStream.open(SocketStream.java:65) ~[mongodb-driver-core-4.4.0.jar:na]
... 4 common frames omitted
Any suggestions on how to do this? Is @DataMongoTest
testing in the wrong way? I thought the integrated flapdoodle dependency would inject it automatically in the test case.
The pom itself is also not that complex:
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.github.openjson</groupId>
<artifactId>openjson</artifactId>
<version>1.0.11</version>
</dependency>
<dependency>
<groupId>com.github.erosb</groupId>
<artifactId>everit-json-schema</artifactId>
<version>1.14.0</version>
</dependency>
</dependencies>
CodePudding user response:
The documentation states:
2.2.4. Embedded Mongo
Spring Boot offers auto-configuration for Embedded Mongo. To use it in your Spring Boot application, add a dependency on de.flapdoodle.embed:de.flapdoodle.embed.mongo and set the spring.mongodb.embedded.version property to match the version of MongoDB that your application will use in production. The default download configuration allows access to most of the versions listed in Embedded Mongo’s Version class as well as some others. Configuring an inaccessible version will result in an error when attempting to download the server. Such an error can be corrected by defining an appropriately configured DownloadConfigBuilderCustomizer bean.
The port that Mongo listens on can be configured by setting the spring.data.mongodb.port property. To use a randomly allocated free port, use a value of 0. The MongoClient created by MongoAutoConfiguration is automatically configured to use the randomly allocated port. If you do not configure a custom port, the embedded support uses a random port (rather than 27017) by default.
If you have SLF4J on the classpath, the output produced by Mongo is automatically routed to a logger named org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongo.
You can declare your own IMongodConfig and IRuntimeConfig beans to take control of the Mongo instance’s configuration and logging routing. The download configuration can be customized by declaring a DownloadConfigBuilderCustomizer bean.
Therefore add the below dependency in scope:test if you want it to only be applied for Tests.
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
As well you need to set the version in your application.properties file:
spring.mongodb.embedded.version=4.0.21
https://docs.spring.io/spring-boot/docs/current/reference/html/data.html#data.nosql.mongodb.embedded
Working Example:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.mongodb.embedded</groupId>
<artifactId>mongodb-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mongodb-app</name>
<description>Demo project for usage of embedded mongodb</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>test</goal>
</goals>
<phase>integration-test</phase>
<configuration>
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/*IT.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
UserRepository:
public interface UserRepository extends MongoRepository<User, Long> {
//Spring converts this to Regex findByFirstnameRegex(String firstname) {"firstname" : {"$regex" : firstname }}
// automatically
public List<User> findByFirstName(String firstName);
}
User:
@Data
@Builder
public class User {
@Id
private Long id;
private String firstName;
private String lastName;
}
Test:
@DataMongoTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class UserControllerIT {
@Autowired
private UserRepository userRepository;
@BeforeAll
public void setup(){
userRepository.save(User.builder().id(1L).firstName("James").lastName("Bond").build());
userRepository.save(User.builder().id(2L).firstName("James").lastName("Farley").build());
userRepository.save(User.builder().id(3L).firstName("Marley").lastName("Hemp").build());
userRepository.save(User.builder().id(4L).firstName("James").lastName("Bond").build());
}
@Test
public void test_getById_successfull() throws Exception {
Assertions.assertEquals("James", userRepository.findByFirstName("James").get(0).getFirstName());
}
}
src/test/resources/application.properties
spring.data.mongodb.database=test
spring.data.mongodb.port=27017
spring.mongodb.embedded.version=4.0.2
CodePudding user response:
Thanks for your answer.
I tried this, but it seems that the versions are not compatible:
This is my application.properties
server.port=8085
spring.data.mongodb.uri=${MONGO_URI}
logging.level.root=INFO
spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-requestsize=20MB
server.error.include-message=always
spring.mongodb.embedded.version=4.0.21
Here I added flapdoodle to the pom
...
<artifactId>everit-json-schema</artifactId>
<version>1.14.0</version>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
But then A lot of class loader errors appearing if I start the unit test
...
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:121) ~[spring-boot-test-2.6.2.jar:2.6.2]
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) ~[spring-test-5.3.14.jar:5.3.14]
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) ~[spring-test-5.3.14.jar:5.3.14]
... 72 common frames omitted
Caused by: java.lang.IllegalStateException: Failed to introspect Class [org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@659e0bfd]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:481) ~[spring-core-5.3.14.jar:5.3.14]
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:358) ~[spring-core-5.3.14.jar:5.3.14]
at org.springframework.util.ReflectionUtils.getUniqueDeclaredMethods(ReflectionUtils.java:414) ~[spring-core-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.lambda$getTypeForFactoryMethod$2(AbstractAutowireCapableBeanFactory.java:765) ~[spring-beans-5.3.14.jar:5.3.14]
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) ~[na:na]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:764) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:703) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:674) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1670) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:570) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:542) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.collectBeanNamesForType(OnBeanCondition.java:238) ~[spring-boot-autoconfigure-2.6.2.jar:2.6.2]
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getBeanNamesForType(OnBeanCondition.java:231) ~[spring-boot-autoconfigure-2.6.2.jar:2.6.2]
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getBeanNamesForType(OnBeanCondition.java:221) ~[spring-boot-autoconfigure-2.6.2.jar:2.6.2]
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getMatchingBeans(OnBeanCondition.java:169) ~[spring-boot-autoconfigure-2.6.2.jar:2.6.2]
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getMatchOutcome(OnBeanCondition.java:144) ~[spring-boot-autoconfigure-2.6.2.jar:2.6.2]
at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47) ~[spring-boot-autoconfigure-2.6.2.jar:2.6.2]
... 88 common frames omitted
Caused by: java.lang.NoClassDefFoundError: de/flapdoodle/embed/process/distribution/IVersion
at java.base/java.lang.ClassLoader.defineClass1(Native Method) ~[na:na]
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1012) ~[na:na]
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[na:na]
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862) ~[na:na]
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760) ~[na:na]
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) ~[na:na]
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[na:na]
at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3402) ~[na:na]
at java.base/java.lang.Class.getDeclaredMethods(Class.java:2504) ~[na:na]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463) ~[spring-core-5.3.14.jar:5.3.14]
... 104 common frames omitted
Caused by: java.lang.ClassNotFoundException: de.flapdoodle.embed.process.distribution.IVersion
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[na:na]
... 117 common frames omitted