Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test a Spring Boot MongoRepository?

In my Spring Boot web application I use MongoDB to store data. In the application I access the database using interfaces that extend MongoRepository.

How do I set up a unit test for such a repository class? What I would like is to

  • start an embedded/in memory instance of MongoDB
  • insert testdata from JSON or XML
  • use an autowired repository to perform queries on the testdata

I have tried using Embedded MongoDB, but I can't figure out how to insert testdata from a file. I've also tried using NoSQLUnit, but the SpringApplicationConfiguration conflicts with the unit test configuration, resulting in different databases for reading and writing.

like image 691
Johanneke Avatar asked Aug 12 '15 16:08

Johanneke


People also ask

What is testentitymanager in Spring Boot?

The Spring Boot TestEntityManager is an alternative to the standard JPA EntityManager that provides methods commonly used when writing tests. EmployeeRepository is the component that we are going to test. Now let's write our first test case:

What is mongorepository in Spring Boot?

MongoRepository is an interface provided by Spring Data in the package org.springframework.data.mongodb.repository. MongoRepository extends the PagingAndSortingRepository and QueryByExampleExecutor interfaces that further extend the CrudRepository interface.

What is integration testing in Spring Boot?

Integration Testing With @SpringBootTest As the name suggests, integration tests focus on integrating different layers of the application. That also means no mocking is involved. Ideally, we should keep the integration tests separated from the unit tests and should not run along with the unit tests.

What is this Spring Boot unit test tutorial about?

This tutorial provides you with these mechanics and goes into the technical details that are necessary to write good unit tests with a focus on Spring Boot applications.


5 Answers

An update for 2018 and Spring Boot 2. First of all, you can have data-only integration tests with the embedded db as per the documentation. This uses the DataMongoTest annotation. This configures only the necessary dependencies that make mongoDB tests possible.

If you want to do full integration tests, add the AutoConfigureDataMongo annotation instead:

@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @AutoConfigureDataMongo public class PriceApiControllerIT { 

Dependencies you should have in your pom.xml:

<parent>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-parent</artifactId>     <version>2.0.1.RELEASE</version> </parent> <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-test</artifactId>         <scope>test</scope>     </dependency>     <dependency>         <groupId>de.flapdoodle.embed</groupId>         <artifactId>de.flapdoodle.embed.mongo</artifactId>         <scope>test</scope>     </dependency> </dependencies> 
like image 177
Nikolaos Georgiou Avatar answered Sep 25 '22 08:09

Nikolaos Georgiou


Just use @DataMongoTest from Spring Boot.

@RunWith(SpringRunner.class) @DataMongoTest public class FooRepositoryTest {      @Autowired     FooRepository fooRepository;      @Before     public void setUp() throws Exception {         fooRepository.save(new Foo());     }      @Test     public void shouldBeNotEmpty() {         assertThat(fooRepository.findAll()).isNotEmpty();     } } 

Dependencies

<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> 
like image 37
MariuszS Avatar answered Sep 26 '22 08:09

MariuszS


I faced the same problem and we used a separate MongoConfiguration class to specify a particular configuration for our tests.

You can create an embedded mongo instance by using EmbeddedMongoBuilder like this :

import com.mongodb.Mongo;
import cz.jirutka.spring.embedmongo.EmbeddedMongoBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.net.ServerSocket;

@Configuration
public class MongoConfiguration {

    @Bean
    public Mongo mongo() throws IOException {
        System.setProperty("DB.TRACE","true");
        return new EmbeddedMongoBuilder()
            .version("2.13.1")
            .bindIp("127.0.0.1")
            .port(allocateRandomPort())
            .build();
    }
}

Then in your test class, specify that you want to use that particular configuration with the @Import annotation :

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@Import(MongoConfiguration.class)

Hope that helps.

like image 37
afraisse Avatar answered Sep 26 '22 08:09

afraisse


This is what I did.

public interface UserRepository extends MongoRepository<Users, Long> {

    public void deleteByUserId(String userId);

    public List<Users> findAllByUserStatus(String userStatus);
}

@Document
public class Users {

    @Id
    private long id;

    @Transient
    public static final String SEQUENCE_NAME = "users_sequence";

    @Indexed
    @NotNull
    private String userId;

    private String firstName;
    private String lastName;
    private String userType;
    private String userStatus;

    @Email
    private String emailId;

    @Size(min = 10, max = 10)
    @NumberFormat
    private String phoneNumber;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getUserType() {
        return userType;
    }

    public void setUserType(String userType) {
        this.userType = userType;
    }

    public String getEmailId() {
        return emailId;
    }

    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public static String getSequenceName() {
        return SEQUENCE_NAME;
    }

    public String getUserStatus() {
        return userStatus;
    }

    public void setUserStatus(String userStatus) {
        this.userStatus = userStatus;
    }

}

Here is the junit

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MockodsApplication.class)
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerIT {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    MongoTemplate mongoTemplate;

    @Autowired
    private MockMvc mvc;

    @After
    public void tearDown() {

    }

    @Test
    public void test1() {
        Users user = new Users();
        long userId = 1L;
        user.setId(userId);
        user.setFirstName("FirstName");
        user.setLastName("FirstName");
        user.setEmailId("[email protected]");
        user.setPhoneNumber("1234567890");

        assertEquals(user, userRepository.save(user));
    }

    @Test
    public void test2() {
        List<Users> persistedUser = userRepository.findAll();
        assertEquals("[email protected]", persistedUser.get(0).getEmailId());
    }
}

This link helped me to implement https://dzone.com/articles/spring-integration-tests

like image 28
Pradeep Sreeram Avatar answered Sep 24 '22 08:09

Pradeep Sreeram


First, make sure that you have added the following Spring Boot parent to your project:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>

Since we added Spring Boot parent, we can add required dependencies without specifying their versions:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

spring-boot-starter-data-mongodb will enable Spring support for MongoDB

<dependency>
    <groupId>de.flapdoodle.embed</groupId>
    <artifactId>de.flapdoodle.embed.mongo</artifactId>
    <scope>test</scope>
</dependency>

de.flapdoodle.embed.mongo provides embedded MongoDB for integration tests

After adding de.flapdoodle.embed.mongo dependency Spring Boot will automatically try to download and start the embedded MongoDB when running tests. The following snippet shows how you can configure the embedded MongoDB instance manually

class EmbeddedMongoDbIntegrationTest {
    private MongodExecutable mongodExecutable;
    private MongoTemplate mongoTemplate;

    @After
    void clean() {
        mongodExecutable.stop();
    }

    @Before
    void setup() throws Exception {
        String ip = "localhost";
        int port = 27017;

        IMongodConfig mongodConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION)
            .net(new Net(ip, port, Network.localhostIsIPv6()))
            .build();

        MongodStarter starter = MongodStarter.getDefaultInstance();
        mongodExecutable = starter.prepare(mongodConfig);
        mongodExecutable.start();
        mongoTemplate = new MongoTemplate(new MongoClient(ip, port), "test");
    }

    @Test
    void test() throws Exception {
        // given
        DBObject objectToSave = BasicDBObjectBuilder.start()
            .add("key", "value")
            .get();

        // when
        mongoTemplate.save(objectToSave, "collection");

        // then
        assertThat(mongoTemplate.findAll(DBObject.class, "collection")).extracting("key")
            .containsOnly("value");
    }
}

Note, that we can quickly create MongoTemplate bean configured to use our manually configured embedded database and register it inside the Spring container, so your mongo repository will start leveraging this mongoTemplate as well.

like image 44
Ashraf Sarhan Avatar answered Sep 24 '22 08:09

Ashraf Sarhan