'Spring Boot Test: bean not injected

Here my test location:

src/test/java
└── cat
    └── gencat
        └── catsalut
            └── hes
                └── mpi
                    └── unit
                        └── mapper
                            └── PatientMapperTest.java

PatientMapperTest.java is:

@SpringBootTest
@RequiredArgsConstructor
public class Patient {
    
    private final TypesMapper typesMapper;

    @Test
    void test() {
        assertNull(this.typesMapper);
    }

}

I've also tried this code (deleting lombok annotation and explicitly initilize typesMapper field):

@SpringBootTest
public class PatientMapperTest {
    
    private final TypesMapper typesMapper;

    public PatientMapperTest(TypesMapper typesMapper) {
        this.typesMapper = new TypesMapperImpl();
    }

    @Test
    void test() {
        assertNull(this.typesMapper);
    }

}

I behaves exactly as before.

Nevertheless, when I perform mvn test, it seems to be ignored:

$ mvn test
[INFO] 
[INFO] Results:
[INFO] 
[ERROR] Errors: 
[ERROR]   PatientMapperTest.test » ParameterResolution No ParameterResolver registered f...
[INFO] 
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0

Related pom dependency:

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Any ideas?



Solution 1:[1]

Unlike your production code, where you don't have to annotate your public constructor with @Autowired, you need it when you want constructor injection for your tests with JUnit 5.

The JUnit Jupiter SpringExtension (automatically registered with @SpringBootTest) ensures to resolve any parameter that is annotated with @Autowired. If you don't add @Autowired, JUnit doesn't know how to resolve this parameter as no one feels responsible as the Spring extension only kicks in when @Autowired (or another identifier) is used:

    @SpringBootTest
    public class PatientMapperTest {
        
        private final TypesMapper typesMapper;
    
        @Autowired
        public PatientMapperTest(TypesMapper typesMapper) {
            this.typesMapper = typesMapper;
    
            // the original assignment doesn't make sense, why inject the instance and then manually call the constructor?
            // this.typesMapper = new TypesMapperImpl();
        }
    
        @Test
        void test() {
            assertNull(this.typesMapper);
        }
    
    }

As an alternative, you can also use the @TestConstructor annotation:

    @SpringBootTest
    @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
    public class PatientMapperTest {
        
        private final TypesMapper typesMapper;
    
        public PatientMapperTest(TypesMapper typesMapper) {
          this.typesMapper = typesMapper;
        }
    }

If you want to dive deeper and understand when the SpringExtension resolves a parameter from the TestContext, take a look at SpringExtension#supportsParameter.

You can also safely use field injection for your tests:

    @SpringBootTest
    public class PatientMapperTest {
         
        @Autowired
        private TypesMapper typesMapper;
    
    
        @Test
        void test() {
            assertNull(this.typesMapper);
        }
    
    }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 helle