Home > OS >  How to use embedded Mongo with Springboot 2.6.2
How to use embedded Mongo with Springboot 2.6.2

Time:01-20

I'm using Spring Boot in version 2.6.2 and Java 17 and try to test my MongoConnection without having a MongoDBService running, because it should be tested with embedded in memory Mongo on the build machine, no need to setup extra mongo DB service there. For sure on productive system it should use a full MongoDB.

I try to bring 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 finde, because a mongo db is running at the specified location. But for test I want to let it run as inmemory 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 how to do this, is @DataMongoTest the wrong way? I thought the integrated flapdoodle dependency would inject it automatically in 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>

Thanks for help in advance, I hope someone can help me.

Thanks and best Lobo

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

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

  •  Tags:  
  • Related