Mapstruct映射器在使用Mockito时返回null对象,尽管已经模拟(mock)。

huangapple go评论68阅读模式
英文:

Mapstruct mapper using mockito returning null object inspite being mocked

问题

I have a mapper which converts one type to another type. I am writing a unit test for the business (service layer) where I am autowiring the mapper. In the corresponding Junit of the service class, I am mocking the mapper using the Mock annotation. While debugging, I could see that the mapper is getting mocked, but after debugging goes to the service layer, the mocked mapper method is returning a null object.

Mapper class:

@Mapper(componentModel = "spring")
public interface EmployeeHolidayMapper {

    @Mapping(target = "employeeEntity.empId", source = "employeeHolidayDto.empId")
    @Mapping(target = "leaveStartDate", expression = "java(LocalDate.parse(employeeHolidayDto"
            + ".getEmpLeaveStartDate().toString(), dateTimeFormatter))")
    @Mapping(target = "leaveEndDate", expression = "java(LocalDate.parse(employeeHolidayDto"
            + ".getEmpLeaveEndDate().toString(), dateTimeFormatter))")
    public EmployeeHolidayEntity employeeHoliDayDtoToEmployeeHolidayEntity(EmployeeHolidayDto
            employeeHolidayDto, LocalDate localDate, DateTimeFormatter dateTimeFormatter);
}

Service class:

@Service
@RequiredArgsConstructor
public class EmployeeHolidayService {

    private final HolidayRepo holidayRepo;
    
    private final EmployeeHolidayRepo employeeHolidayRepo;
    
    private final EmployeeHolidayMapper employeeHolidayMapper;

    // Rest of the service class code...
}

JUnit class using Mockito:

@ExtendWith(MockitoExtension.class)
class HolidaymanagementApplicationTests {

    @Mock
    HolidayRepo holidayRepo;

    @Mock
    EmployeeHolidayRepo employeeHolidayRepo;

    @Mock
    EmployeeHolidayMapper employeeHolidayMapper;

    @InjectMocks
    EmployeeHolidayService employeeHolidayService;

    // Rest of the JUnit test code...
}

In the above code, while debugging, I could see that the mapper is mocked. Now when the implementation goes to the service class from the assertEquals statement, the EmployeeHolidayEntity object fetched through the mapper is returning null.

When running the code as a Spring Boot application, the mapper is working fine and returning the object.

Below is my entity class:

@Entity
@Table(name = "empl_holiday_tbl")
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
public class EmployeeHolidayEntity implements Serializable {

    private static final long serialVersionUID = 2429371741034836686L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", columnDefinition = "INT UNSIGNED AUTO_INCREMENT")
    private Integer employeeHolidayId;

    @Column(name = "startDate", columnDefinition = "DATE NOT NULL")
    private LocalDate leaveStartDate;

    @Column(name = "endDate", columnDefinition = "DATE NOT NULL")
    private LocalDate leaveEndDate;

    @ManyToOne
    @JoinColumn(name = "empId", nullable = false, columnDefinition = "INT UNSIGNED")
    private EmployeeEntity employeeEntity;
}
英文:

I have a mapper which converts one type to another type. I am writing a unit test forbusiness (service layer) where I am autowiring mapper. In the corresponding Junit of service class I am mocking mapper using Mock annotation. While debugging I could see that mapper is getting mocked but after debugging goes to service layer the mocked mapper method is returning null object

Mapper class

@Mapper(componentModel = "spring")
public interface EmployeeHolidayMapper {

	@Mapping(target = "employeeEntity.empId",source = "employeeHolidayDto.empId")
	@Mapping(target = "leaveStartDate",expression = "java(LocalDate.parse(employeeHolidayDto"
			+ ".getEmpLeaveStartDate().toString(),dateTimeFormatter))")
	@Mapping(target = "leaveEndDate",expression = "java(LocalDate.parse(employeeHolidayDto"
			+ ".getEmpLeaveEndDate().toString(),dateTimeFormatter))")
	public EmployeeHolidayEntity employeeHoliDayDtoToEmployeeHolidayEntity(EmployeeHolidayDto 
			employeeHolidayDto,LocalDate localDate,DateTimeFormatter dateTimeFormatter);

Service class

@Service
@RequiredArgsConstructor
public class EmployeeHolidayService {

	
	private final HolidayRepo holidayRepo;
	
	private final EmployeeHolidayRepo employeeHolidayRepo;
	
	private final EmployeeHolidayMapper employeeHolidayMapper;
	

	public Mono<Integer> saveEmployeeHolidayData(EmployeeHolidayDto employeeHolidayDto){
		return	Optional.ofNullable(employeeHolidayDto)
		.filter(dto->dto.getEmpLeaveEndDate()!=null && dto.getEmpLeaveStartDate() != null 
		   && dto.getEmpLeaveStartDate() > 0 && dto.getEmpLeaveEndDate() > 0 && dto.getEmpLeaveEndDate()
		   .compareTo(dto.getEmpLeaveStartDate()) >=0)
		.map(dto->{
				EmployeeHolidayEntity employeeHolidayEntity = employeeHolidayMapper
						.employeeHoliDayDtoToEmployeeHolidayEntity(dto, LocalDate.now(),
								DateTimeFormatter.BASIC_ISO_DATE);
				int diffOfDays = employeeHolidayEntity.getLeaveEndDate().getDayOfYear()
						- employeeHolidayEntity.getLeaveStartDate().getDayOfYear();
				int noOfLeavesLeft = holidayRepo.getNoofLeavesLeft(dto.getEmpId()) - (diffOfDays);
				if (noOfLeavesLeft > 0) {
					int updatedRows = holidayRepo.updateNoLeaveDays(dto.getEmpId(), noOfLeavesLeft);
					if (updatedRows > 0) {
						EmployeeHolidayEntity employeeHolidayEntitySaved = employeeHolidayRepo
								.saveAndFlush(employeeHolidayEntity);
						if (employeeHolidayEntitySaved.getEmployeeHolidayId() > 0) {
							return Mono.just(updatedRows);
						} else {
							return Mono.just(0);
						}
					} else {
						return Mono.just(0);
					}
				}
				else {
					return Mono.just(0);
				}
			
		})
		.orElse(null);
	}

Junit class using mockito

@ExtendWith(MockitoExtension.class)
class HolidaymanagementApplicationTests {

	@Mock
	HolidayRepo holidayRepo;

	@Mock
	EmployeeHolidayRepo employeeHolidayRepo;

	@Mock
	EmployeeHolidayMapper employeeHolidayMapper;

	@InjectMocks
	EmployeeHolidayService employeeHolidayService;

	@Test
	void testPostrequest() {
		String startDate = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
		String endDate = LocalDate.now().plusDays(2).format(DateTimeFormatter.BASIC_ISO_DATE);
		EmployeeHolidayDto employeeHolidayDto = new EmployeeHolidayDto(1, Integer.parseInt(startDate),
				Integer.parseInt(endDate));
		EmployeeEntity employeeEntity = new EmployeeEntity(1, null, null, null, null);
		EmployeeHolidayEntity employeeHolidayEntity = new EmployeeHolidayEntity(1,
				LocalDate.parse(startDate, DateTimeFormatter.BASIC_ISO_DATE),
				LocalDate.parse(endDate, DateTimeFormatter.BASIC_ISO_DATE), employeeEntity);
		when(employeeHolidayMapper.employeeHoliDayDtoToEmployeeHolidayEntity(employeeHolidayDto, LocalDate.now(),
				DateTimeFormatter.BASIC_ISO_DATE)).thenReturn(employeeHolidayEntity);
		HolidayEntity holidayEntity = new HolidayEntity(1, 10, employeeEntity);
		when(holidayRepo.getNoofLeavesLeft(1)).thenReturn(10);
		when(holidayRepo.updateNoLeaveDays(1, 8)).thenReturn(1);
		EmployeeHolidayEntity employeeHolidayDbEntity = new EmployeeHolidayEntity(1,
				employeeHolidayEntity.getLeaveStartDate(), employeeHolidayEntity.getLeaveEndDate(), employeeEntity);
		when(employeeHolidayRepo.saveAndFlush(employeeHolidayEntity)).thenReturn(employeeHolidayDbEntity);
		assertEquals(employeeHolidayService.saveEmployeeHolidayData(employeeHolidayDto).block(), 
				1);

In the above code while debugging I could see that mapper is mocked. Now when implementation goes to service class from asserEquals statement. The EmployeeHolidayEntity object fetched through mapper is returning null.

When running code a spring boot application mapper is working fine and is returning the object.

Below is my entity class.

@Entity
@Table(name = "empl_holiday_tbl")
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
public class EmployeeHolidayEntity implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 2429371741034836686L;
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "id", columnDefinition = "INT UNSIGNED AUTO_INCREMENT")
	private Integer employeeHolidayId;
	
	@Column(name = "startDate", columnDefinition = "DATE NOT NULL")
	private LocalDate leaveStartDate;
	
	@Column(name = "endDate", columnDefinition = "DATE NOT NULL")
	private LocalDate leaveEndDate;
	
	@ManyToOne
	@JoinColumn(name = "empId",nullable = false,columnDefinition = "INT UNSIGNED")
	private EmployeeEntity employeeEntity;

}

答案1

得分: 1

Your problem is that your mock when( never matches the execution.

`when(employeeHolidayMapper.employeeHoliDayDtoToEmployeeHolidayEntity(employeeHolidayDto, LocalDate.now(),
DateTimeFormatter.BASIC_ISO_DATE)).thenReturn(employeeHolidayEntity);

contains a "LocalDate.now()" your actual execution is a few milliseconds later. It will therefore be different and the when( never is used. The fallback of a mocked class is to return null which is your result.

To verify my suspicion you could try to change your code to

`when(employeeHolidayMapper.employeeHoliDayDtoToEmployeeHolidayEntity(any(), any(),
any())).thenReturn(employeeHolidayEntity);

your return value should now be correct.

Furthermore, I would suggest changing your test setup. For Mockito tests with Mapstruct, I usually use

@Spy
EmployeeHolidayMapper employeeHolidayMapper = Mappers.get(EmployeeHolidayMapper .class);

@InjectMocks
EmployeeHolidayService employeeHolidayService;

this way you get the real mapper since mapping is something that usually don't need mocking. You then don't have to call when( on mappers in your tests.

英文:

Your problem is that your mock when( never matches the execution.

 when(employeeHolidayMapper.employeeHoliDayDtoToEmployeeHolidayEntity(employeeHolidayDto, LocalDate.now(),
            DateTimeFormatter.BASIC_ISO_DATE)).thenReturn(employeeHolidayEntity);

contains a "LocalDate.now()" your actuall execution is a few milisenconds later. It will therefore be different and the when( never is used. The fallback of a mocked class is to return null which is your result.

To verify my suspicion you could try to change your code to

when(employeeHolidayMapper.employeeHoliDayDtoToEmployeeHolidayEntity(any(), any(),
            any())).thenReturn(employeeHolidayEntity);

your return value should now be correct.

Furthermore i would suggest changing your test setup. For Mockito tests with Mapstruct I usually use

@Spy
EmployeeHolidayMapper employeeHolidayMapper = Mappers.get(EmployeeHolidayMapper .class);

@InjectMocks
EmployeeHolidayService employeeHolidayService;

this way you get the real mapper since mapping is something that usually dont need mocking. You then don´t have to call when( on mappers in your tests.

huangapple
  • 本文由 发表于 2023年5月7日 17:24:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76193070.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定