When I run the test code, I get the following exception: java.lang.NullPointerException: Cannot invoke "com.project.clickit.entity.DormitoryEntity.toDTO()" because "this.dormitoryEntity" is null
DormitoryEntity
@Setter
@Getter
@Builder
@Table(name = "dormitory")
@NoArgsConstructor
//@AllArgsConstructor
@Entity
public class DormitoryEntity {
@Id
@Column(name = "dormitory_id")
private String id;
@Column(name = "dormitory_name")
private String name;
@Builder
public DormitoryEntity(String id, String name) {
this.id = id;
this.name = name;
}
public DormitoryDTO toDTO() {
return DormitoryDTO.builder()
.id(this.id)
.name(this.name)
.build();
}
}
FacilityEntity
@Setter
@Getter
@Builder
@Table(name = "facility")
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class FacilityEntity {
@Id
@Column(name = "facility_id")
private String id;
@Column(name = "facility_name")
private String name;
@Column(name = "facility_info")
private String info;
@Column(name = "facility_open")
private Integer open;
@Column(name = "facility_close")
private Integer close;
@Column(name = "facility_img")
private String img;
@Column(name = "facility_terms")
private String terms;
@Column(name = "facility_extension_limit")
private Integer extensionLimit;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "facility_dormitory")
private DormitoryEntity dormitoryEntity;
public FacilityEntity(String id, String name, String info, Integer open,
Integer close, String img, String terms, Integer extensionLimit) {
this.id = id;
this.name = name;
this.info = info;
this.open = open;
this.close = close;
this.img = img;
this.terms = terms;
this.extensionLimit = extensionLimit;
}
public FacilityDTO toDTO() {
return FacilityDTO.builder()
.id(this.id)
.name(this.name)
.info(this.info)
.open(this.open)
.close(this.close)
.img(this.img)
.terms(this.terms)
.extensionLimit(this.extensionLimit)
.dormitoryDTO(this.dormitoryEntity.toDTO())
.build();
}
}
FacilityRepository
@Query("SELECT new FacilityEntity(f.id, f.name, f.info, f.open, f.close, f.img, f.terms, f.extensionLimit, f.dormitoryEntity) FROM FacilityEntity f where f.id = :id")
FacilityEntity findByFacilityId(@Param("id") String id);
FacilityService
@Transactional
public FacilityDTO findById(String id) {
return facilityRepository.findByFacilityId(id).toDTO();
}
Test Code
@Slf4j
@ExtendWith({MockitoExtension.class})
public class FacilityServiceTest {
@Mock
private FacilityRepository facilityRepository;
@InjectMocks
private FacilityService facilityService;
@Test
void findByIdTest(){
given(facilityRepository.findByFacilityId("test_facility")).willReturn(new FacilityEntity());
FacilityDTO result = facilityService.findById("test_facility");
assertThat(result).isNotNull;
}
}
How can I solve this problem?
I tried the following things to solve this problem: add @Transactional annotation after @Test annotation(In detail, @Transactional(propagation = Propagation.SUPPORT)), edit fetchType LAZY to EAGER
*(Edit) When a Get request is sent using Postman, findById in FacilityService operates normally. So, This seems to happen because FacilityRepository is a Mock object, not a real object. But I still don't know how to solve this problem.
Problem is, the mock behavior is to return a facility but doesn't initialise the dormatory value;
.willReturn(new FacilityEntity());
As your service findById
method invokes FacilityEntity.toDTO()
-> DormatoryEntity.toDTO()
and the reference to dormatory is null
you end up with the NPE.
Initialising that value should fix it, something like this:
var facility = new FacilityEntity();
var dormatory = new DormitoryEntity();
// init any data you want
facility.setDormitoryEntity(dormatory);
given(facilityRepository.findByFacilityId("test_facility"))
.willReturn(facility);
Alternatively, if your data is likely to have facilities with no dormatories you can null-check the DormatoryEntity in your FacilityDTO toDTO()
method.
You may also consider hooking your tests up to a in-memory (or even test container) database as this can help highlight other hibernate related issues.