【问题标题】:MapStruct mapper not initialized with autowired when debug调试时未使用自动装配初始化 MapStruct 映射器
【发布时间】:2020-11-30 05:47:18
【问题描述】:

我使用带有 mapstruct 的 spring boot 2.3.2。

在一个服务类中,我有一个具有自动装配注释的映射器。

@Service
public BillingService{

    private BillingRepository billingRepository;


    @Autowired
    private  BillingMapper billingMapper;   


    @Autowired
    public BillingService (BillingRepository billingRepository){
        this.billingRepository=billingRepository;
    }

    public void getBilling(Long billingId){
        ....
        billingMapper.convertTo(...);
    }

}


@RunWith(MockitoJUnitRunner.class)
public class BillingServiceTest{
    @Mock
    BillingRepository billingRepository;

    private BillingService bilingService;

    @Spy
    private BillingMapper billingMapper = Mappers.getMapper(BillingMapper.class);

    @BeforeEach
    public void setup(){
        MockitoAnnotations.initMocks(this);
        billingService = new BillingService();
    }

    @Test
    public void testGetBilling(){

        List<Billing> billings = new ArrayList<>();
        ...

        List<BillingPayload> payloads = new ArrayList<>();

        when(billingMapper.convertTo(billings)).thenReturn(payloads);     

        bilingService.getBilling(1l);  
    }

}

@Mapper(componentModel="spring")
public interface BillingMapper{
    ...
}

当我调试时卡在 BillingService 类的 getBilling 方法中,billingMapper 始终为空;

【问题讨论】:

    标签: spring spring-boot mockito mapstruct


    【解决方案1】:

    您正在使用非常奇怪的配置。首先,您有返回 BillingService 的方法,该方法未指定任何返回值,因此甚至无法编译。我建议以下方式:

    @Service
    public BillingService{
    
        private final BillingRepository billingRepository;
        private final BillingMapper billingMapper;
    
        // constructor with bean injection
        public BillingService(final BillingRepository billingRepository,
                              final BillingMapper billingMapper) {
            this.billingRepository = billingRepository;
            this.billingMapper = billingMapper;
        }
    
        public void getBilling(Long billingId){
            ....
            billingMapper.convertTo(...);
        }
    
    }
    

    然后你可以像下面这样配置你的测试:

    @RunWith(SpringJUnit4ClassRunner.class)
    public class BillingServiceTest {
    
        @Spy private BillingMapper billingMapper = Mappers.getMapper(BillingMapper.class);
        @MockBean private BillingRepository billingRepository;
        @Autowired private BillingService billingService;
    
        @TestConfiguration
        static class BillingServiceTestContextConfiguration {
            @Bean public BillingService billingService() {
                return new BillingService(billingRepository, billingMapper);
            }
        }
    
        @Test
        public void testGetBilling(){
            List<Billing> billings = new ArrayList<>();
            ...
            List<BillingPayload> payloads = new ArrayList<>();
            when(billingRepository.findById(1L)).thenReturn(); // someResult
            when(billingMapper.convertTo(billings)).thenReturn(payloads);
            bilingService.getBilling(1l);  
        }
    }
    
    @Mapper(componentModel="spring")
    public interface BillingMapper{
        ...
    }
    

    类似的配置应该可以工作。主要问题是您将@Autowired 与setter/constructor 注入混合在一起(不知道因为BillingService 中的奇怪方法。也不知道为什么在尝试模拟接口时使用@Spy 注释。@Spy用于模拟实际实例,而@Mock 用于模拟类类型。如果在您的应用程序中将BillingMapper 指定为Bean,我会坚持使用@MockBean private BillingMapper billingMapper

    【讨论】:

    • 奇怪的方法是我的构造函数出错,我没有为 BillingMapper 手动创建任何 bean。得到同样的错误,用 spy、mock、mockbean 测试......通过控制器测试需要更少的时间
    猜你喜欢
    • 2021-12-04
    • 1970-01-01
    • 2023-01-31
    • 2020-08-02
    • 2020-01-14
    • 1970-01-01
    • 2021-05-19
    • 2016-08-15
    • 1970-01-01
    相关资源
    最近更新 更多