Search code examples
javaspring-bootjunitdependency-injectionmapstruct

Mapstruct Abstract mapper cannot mock component in unit test


With MapStruct, I created a mapper that is an abstract class. I deciced to transform the mapper from interface to abstract, in order to use a component names AddressConverter that itself is using a component named CountryService.

Even though the mapping works fine, on unit test it complains about the component AddressConverter that cannot find a qualifying bean.

I tried adding it to ContextConfiguration of the mapper, but the issue will chain to the nested component up until the repository which I cannot add it to ContextConfiguration since it's an interface.

The exception

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.mind.microservice.mapper.converter.AddressConverter' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1509)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584)
    ... 42 more

Mapper class. I tried adding AddressConverter to the uses attribute on @Mapper annotation. But the exception moved to the next component of AddressConverter as I mentioned above.

@Mapper(componentModel = "spring", uses = {
        GenericMapper.class,
        Size.class
})
public abstract class StudentMapper{

    @Autowired
    private AddressConverter addressConverter;

    @Mappings({
            @Mapping(target = "address", source = "student", qualifiedByName = "formatAddress"),
    })
    public abstract StudentEntity map(Student student);

    @Named("formatAddress")
    public String formatAddress(Student student){
        return this.addressConverter.buildAddress(student);
    }
    
}

AddressCoverter

@Component
@AllArgsConstructor
public class AddressConverter {

    private final CountryService countryService;

    public String buildAddress(Student student){
        return this.countryService.countryFormatter(student.getCountry); 
    }
}

The test class that the exception appears. As I mentioned, I tried adding AddressConverter to ContextConfiguration. I also tried mocking it completely by adding InjectMocks.

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {
        GenericMapper.class,
        Size.class
})
public class StudentMapperTest{

    @Autowired
    private StudentMapper MAPPER;
     
    //also @Autowired was used and also I removed it completely, still the same exception
    @InjectMocks 
    private AddressConverter addressConverter;

    @Test
    public void testStudentToStudentEntityMapping() {
        Student randomStudent = ObjectHandler.random(Student.class);

      //...the rest of the test but it doesn't even enter, so it doesn't affect the outcome.
   }
 
}

Solution

  • I belive you need to change @InjectMock with @MockBean