模拟存储库函数会导致空指针异常,尽管使用了when和thenReturn



我的ServiceImpl看起来像这样:

@Service
public class GuildUsersServiceImpl implements GuildUsersService {
@Autowired
private final GuildUsersRepository guildUsersRepository;
@Autowired
private GuildService guildService;
@Autowired
private UserService userService;
@Autowired
private GuildUserRoleTypeService guildUserRoleTypeService;
@Autowired
public GuildUsersServiceImpl(final GuildUsersRepository guildUsersRepository) {
this.guildUsersRepository = guildUsersRepository;
}
public GuildUsers create(GuildUsers guildUser) {
return this.guildUsersRepository.save(guildUser);
}
}

我对create的服务测试如下:

@RunWith(SpringRunner.class)
public class GuildUsersServiceTest {

@Mock private GuildUsersRepository guildUsersRepository;

@Mock private UserService userService;
@Mock private GuildService guildService;

@Mock private GuildUserRoleTypeService guildUserRoleTypeService;
@InjectMocks private GuildUsersServiceImpl guildUsersService;

@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}

@Test
public void createTest() {
GuildUsers guildUsers = mockGuildUsers();
when(guildUsersRepository.save(guildUsers)).thenReturn(guildUsers);
GuildUsers dbGuildUsers = guildUsersService.create(guildUsers);
assertEquals(dbGuildUsers.getId(),guildUsers.getId());
}
}

我正在使用jUnit4。尽管在测试中使用了when(guildUsersRepository.save(guildUsers)).thenReturn(guildUsers);,但我还是遇到了一个带有以下跟踪的空指针异常:

java.lang.NullPointerException: Cannot invoke "com.project.entities.domain.GuildUsers.getId()" because "dbGuildUsers" is null

我怀疑我嘲笑@Autowired类的方式有问题。在测试类中模拟类时,需要做哪些不同的操作?

同时,我希望以下测试用例中的服务也能正常工作:

@Test
public void createTest1() {
GuildUsers guildUsers = mockGuildUsers();
GuildUsersWithoutGuildIdRequestDTO guildUsersWithoutGuildIdRequestDTO = new  GuildUsersWithoutGuildIdRequestDTO();
guildUsersWithoutGuildIdRequestDTO.setUserId(guildUsers.getUser().getId());
guildUsersWithoutGuildIdRequestDTO.setGuildUserRoleTypeId(guildUsers.getGuildUserRoleType().getId());

when(guildService.get(guildUsers.getGuild().getId())).thenReturn(Optional.of(guildUsers.getGuild()));
when(guildRepository.findById(any())).thenReturn(Optional.of(guildUsers.getGuild()));
when(userService.get(guildUsers.getUser().getId())).thenReturn(Optional.of(guildUsers.getUser()));
when(guildUsersRepository.findById(guildUsers.getId())).thenReturn(null);
when(guildUserRoleTypeService.get(guildUsers.getGuildUserRoleType().getId())).thenReturn(Optional.of(guildUsers.getGuildUserRoleType()));
when(guildUsersRepository.save(any())).thenReturn(guildUsers);

GuildUsersResponseDTO dbGuildUsersResponseDTO = 
guildUsersService.create(guildUsers.getGuild().getId(),guildUsersWithoutGuildIdRequestDTO);
assertEquals(dbGuildUsersResponseDTO.getGuildUserRoleType(),guildUsers.getGuildUserRoleType());
}

我不知道为什么同时进行字段注入和构造函数注入。您应该先删除其中一个,例如:

@Service
public class GuildUsersServiceImpl implements GuildUsersService {

//@Autowired - removed
private final GuildUsersRepository guildUsersRepository;
@Autowired
private GuildService guildService;
@Autowired
private UserService userService;
@Autowired
private GuildUserRoleTypeService guildUserRoleTypeService;
//@Autowired - removed
public GuildUsersServiceImpl(final GuildUsersRepository guildUsersRepository) {
this.guildUsersRepository = guildUsersRepository;
}
public GuildUsers create(GuildUsers guildUser) {
return this.guildUsersRepository.save(guildUser);
}
}

或者删除要添加的构造函数:

@Autowired 
private final GuildUsersRepository guildUsersRepository;```

InjectMock文档清楚地说:"Mockito将尝试仅通过构造函数注入、setter注入或属性注入按顺序注入mock,如下所述">

在您的案例中,它显然选择了属性注入,因此guildUserRoleTypeService仍然未初始化。如上所述,混合属性和构造函数注入不是一个好主意。

为了改进GuildUsersServiceImpl的总体设计和可测试性,我建议坚持使用构造函数注入。通过这种方式,您可以:a( 将所有字段声明为private final,因此在创建对象后将始终设置这些字段,从而避免竞争条件和NPE。b( 在测试中使用适当的构造函数而不是InjectMocks初始化GuildUsersServiceImpl。

把这个留给其他人,他们也浪费了一个下午的时间试图让一个存储库返回他们配置的内容。最终我不得不做的事情是,我必须使用@MockBean注释Mockito/Spring组合才能正常工作,而不是用@Mock注释存储库。

根据@MockBean:的javadoc

当@MockBean被用于字段,以及在应用程序上下文中注册时,mock也将被注入到字段中。

最新更新