Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make the junit tests use the embedded mongoDB in a springboot application?

I am learning springboot and have created a simple springboot application. I want it to use the embedded mongoDB when it runs the unit tests and the external mongoDB for the rest of the application. However it uses the external mongoDB for the unit tests instead of the embedded one. I have following two dependencies in my POM.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <dependency>
        <groupId>de.flapdoodle.embed</groupId>
        <artifactId>de.flapdoodle.embed.mongo</artifactId>
        <scope>test</scope>
    </dependency>

my properties file has the following:

# MongoDB properties
mongo.db.name=person_testDB
mongo.db.url=localhost

#external Mongo url
spring.data.mongodb.uri=mongodb://localhost:27017/personDB

I have a config file(MongoDBConfig.java) which includes embedded MongoDB configurations:

@EnableMongoRepositories
public class MongoDBConfig {

@Value("${mongo.db.url}")
private String MONGO_DB_URL;

@Value("${mongo.db.name}")
private String MONGO_DB_NAME;

@Bean
public MongoTemplate mongoTemplate() {
    MongoClient mongoClient = new MongoClient(MONGO_DB_URL);
    MongoTemplate mongoTemplate = new MongoTemplate(mongoClient, MONGO_DB_NAME);
    return mongoTemplate;
 }
}

Following is my PersonService.java class:

@Service
public class PersonService  {
private static final Logger logger = LoggerFactory.getLogger(PersonService.class);      
@Autowired
MongoTemplate mongoTemplate;            
public void savePerson(Person person) {
    String name = person.getName();
    String collectionName = name.substring(0, 1);
        mongoTemplate.save(person, collectionName);
    }               
}

My unit test for the PersonsService class is as follows:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {MongoDBConfig.class})
@SpringBootTest(classes = PersonService.class)
@DataMongoTest
public class PersonServiceTest {

@Autowired
PersonService personService;    
MongodForTestsFactory factory;
MongoClient mongo;

@Before
public void setup() throws Exception {
    factory = MongodForTestsFactory.with(Version.Main.PRODUCTION);
    mongo = factory.newMongo();
}

@After
public void teardown() throws Exception {
    if (factory != null)
        factory.shutdown();
}

@Test
public void testSave(){
Person person = new Person("Bob Smith " , 25);
personService.savePerson(person);
}
}

It creates the collection name and the document name correctly in the external MongoDB which is not what I want. How can I restrict the unitTests to an embedded Mongo?

like image 914
SkyBlue Avatar asked Dec 31 '17 03:12

SkyBlue


1 Answers

An alternative would be to run entire spring boot application in test. In this case your spring boot application will be discovered automatically and embedded mongoDB will be downloaded and started by Spring Boot

@RunWith(SpringRunner.class)
@SpringBootTest
public class YourSpringBootApplicationTests {

08:12:14.676 INFO EmbeddedMongo:42 - note: noprealloc may hurt performance in many applications 08:12:14.694 INFO EmbeddedMongo:42 - 2017-12-31T08:12:14.693+0200 I CONTROL [initandlisten] MongoDB starting : pid=2246 port=52299 08:12:22.005 INFO connection:71 - Opened connection [connectionId{localValue:2, serverValue:2}] to localhost:52299

In case of your example you might modify the code in order to start embedded Mongo on different port:

  1. add test/resoures/test.properties file in order to override properties from application.properties

    mongo.db.name=person_testDB
    mongo.db.url=localhost
    mongo.db.port=12345
    
  2. modify MongoDBConfig: add MONGO_DB_PORT field

    @EnableMongoRepositories
    public class MongoDBConfig {
        @Value("${mongo.db.url}")
        private String MONGO_DB_URL;
    
        @Value(("${mongo.db.port:27017}"))
        private int MONGO_DB_PORT;
    
        @Value("${mongo.db.name}")
        private String MONGO_DB_NAME;
    
        @Bean
        public MongoTemplate mongoTemplate() {
            MongoClient mongoClient = new MongoClient(MONGO_DB_URL, MONGO_DB_PORT);
            MongoTemplate mongoTemplate = new MongoTemplate(mongoClient, MONGO_DB_NAME);
            return mongoTemplate;
        }
    }
    
  3. modify test class: remove @DataMongoTest annotation. This annotation forces starting embedded mongoDB instance

    static MongodExecutable mongodExecutable;
    
    @BeforeClass
    public static void setup() throws Exception {
        MongodStarter starter = MongodStarter.getDefaultInstance();
        String bindIp = "localhost";
        int port = 12345;
        IMongodConfig mongodConfig = new MongodConfigBuilder()
                .version(Version.Main.PRODUCTION)
                .net(new Net(bindIp, port, Network.localhostIsIPv6()))
                .build();
        mongodExecutable = null;
        try {
            mongodExecutable = starter.prepare(mongodConfig);
            mongodExecutable.start();
        } catch (Exception e){
            // log exception here
            if (mongodExecutable != null)
                mongodExecutable.stop();
        }
    }
    
    @AfterClass
    public static void teardown() throws Exception {
        if (mongodExecutable != null)
            mongodExecutable.stop();
    }
    

One more way is to use MongoRepository and init embedded Mongo as part of test @Configuration class: it's outlined here: How do you configure Embedded MongDB for integration testing in a Spring Boot application?

like image 97
Alex M981 Avatar answered Oct 29 '22 22:10

Alex M981