1

Problem

Running mvn test returns the following error in the unit test of a Mongo repository:

Error creating bean with name 'my_package.infrastructure.mongo.repositories.UserRepositoryMongoImplTests': Unsatisfied dependency expressed through field 'userRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'my_package.infrastructure.mongo.repositories.users.UserRepositoryMongoImpl' available

As you can see in the code below I made sure to add the @Repository annotation to my implementation class (AKA UserRepositoryMongoImpl), I also tried with the @Component annotation, but it did not work. Then I tried to add the @ComponentScan("my_package") annotation to my configuration class but still without success.

Code

I am using Java 11, Spring Boot, JUnit and TestContainers. My folder structure is as follow:

.
├── src/
│   ├── main/
│   │   └── java/
│   │       └── my_package/
│   │           ├── domain/
│   │           │   └── users/
│   │           │       ├── User.java
│   │           │       └── UserRepository.java
│   │           ├── infrastructure/
│   │           │   └── mongo/
│   │           │       └── repositories/
│   │           │           └── UserRepositoryMongoImpl.java
│   │           └── ApplicationConfig.java
│   └── test/
│       └── my_package/
│           └── infrastructure/
│               └── mongo/
│                   └── repositories/
│                       └── UserRepositoryMongoImplTests.java
└── pom.xml

UserRepository.java

public interface UserRepository {
    public User findByUsername(String username);
    public User save(User user);
    public void delete(User user);
}

ApplicationConfig.java

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("my_package")
public class ApplicationConfig {
}

UserRepositoryMongoImpl.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;

import my_package.domain.users.User;
import my_package.domain.users.UserRepository;

@Repository
public class UserRepositoryMongoImpl implements UserRepository {
    @Autowired
    private MongoTemplate mongoTemplate;

    private static final String COLLECTION_NAME = "users";

    @Override
    public User findByUsername(String username) {
        return mongoTemplate.findOne(Query.query(Criteria.where("username").is(username)), User.class, COLLECTION_NAME);
    }

    @Override
    public User save(User user) {
        return mongoTemplate.save(user, COLLECTION_NAME);
    }

    @Override
    public void delete(User user) {
        mongoTemplate.remove(user, COLLECTION_NAME);
    }
}

UserRepositoryMongoImplTests.java

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

import my_package.domain.users.User;
import my_package.infrastructure.mongo.repositories.users.UserRepositoryMongoImpl;

@Testcontainers
@DataMongoTest(excludeAutoConfiguration = {
    EmbeddedDataSourceConfiguration.class
})
public class UserRepositoryMongoImplTests {
    @Autowired
    private UserRepositoryMongoImpl userRepository;

    @Container
    private MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.4.4"));

    @BeforeEach
    void mongoDbContainerStarting() {
        mongoDBContainer.start();
        System.setProperty("spring.data.mongodb.uri", mongoDBContainer.getReplicaSetUrl());
    }

    @AfterEach
    void mongoDbContainerStoping() {
        mongoDBContainer.stop();
    }

    @Test
    public void shouldSaveValidNewUser() {
        User user = new User("username123", "heuehueheuh");
        user = userRepository.save(user);

        assert(user.id() != null);
        assert(user.username().equals("username123"));
        assert(user.password().equals("heuehueheuh"));
    }
}

And for last: pom.xml

<?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.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>br.nom.carneiro.carlos</groupId>
    <artifactId>future_software_003-ws</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>my-project</name>
    <description>Random description</description>
    <properties>
        <java.version>11</java.version>
        <testcontainers.version>1.15.2</testcontainers.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-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.restdocs</groupId>
            <artifactId>spring-restdocs-mockmvc</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>mongodb</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-guava</artifactId>
            <version>3.4.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.testcontainers</groupId>
                <artifactId>testcontainers-bom</artifactId>
                <version>${testcontainers.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.asciidoctor</groupId>
                <artifactId>asciidoctor-maven-plugin</artifactId>
                <version>1.5.8</version>
                <executions>
                    <execution>
                        <id>generate-docs</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>process-asciidoc</goal>
                        </goals>
                        <configuration>
                            <backend>html</backend>
                            <doctype>book</doctype>
                        </configuration>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.restdocs</groupId>
                        <artifactId>spring-restdocs-asciidoctor</artifactId>
                        <version>${spring-restdocs.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Questions I looked up before

If you can offer me advice, any input will be appreciated.

Edit

I tried to create the Bean manually adding the following code to my configuration class:

    @Bean
    public UserRepositoryMongoImpl userRepositoryMongoImpl() {
        return new UserRepositoryMongoImpl();
    }

Then I got the following error:

The bean 'userRepositoryMongoImpl', defined in class path resource [blablabla], could not be registered. A bean with that name has already been defined in file [blablabla] and overriding is disabled.

This probably means that the bean is being correctly declared through the @Repository annotation, but for some reason Spring is not finding it to inject in my test class.

crocarneiro
  • 134
  • 8

1 Answers1

0

I solved my problem putting the following annotation in my @SpringBootApplication class: @ComponentScan(basePackages = {"my_package.infrastructure.mongo"}). Turns out that my ComponentScan annotation was wrong and in the wrong class.

I reached this conclusion reading the answer for the question NoSuchBeanDefinitionException : no qualifying bean of type.

crocarneiro
  • 134
  • 8