尽管使用了when和thenReturn,但模拟存储库函数会导致空指针异常
Posted
技术标签:
【中文标题】尽管使用了when和thenReturn,但模拟存储库函数会导致空指针异常【英文标题】:Mocking repository function leads to null pointer exception despite using when and thenReturn 【发布时间】:2021-02-07 13:53:29 【问题描述】:我的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);
我对@987654323@ 的服务测试如下所示:
@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 类中模拟类时需要做哪些不同的事情?
同时我希望以下测试用例中的服务也能正常工作:
@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());
【问题讨论】:
【参考方案1】:我不知道您为什么同时进行字段注入和构造函数注入。您应该首先删除其中一个,例如:
@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;```
【讨论】:
那行不通。在任何一种情况下,我都只传递对象的那个实例,因此不需要调用any()
应该如何模拟和注入其他@Autowired
服务,因为虽然您的回答解决了存储库的问题,但其他自动连接的服务开始抛出 NPE
哦,您对测试进行了修改还是按原样进行了修改?
问题出现在不同的测试用例中。在我的问题中添加相同的内容。
你能具体说一下吗?【参考方案2】:
InjectMock 文档明确指出:“Mockito 将尝试仅通过构造函数注入、setter 注入或属性注入按顺序注入模拟,如下所述。”
在您的情况下,它显然选择了属性注入,因此 guildUserRoleTypeService 保持未初始化。如上所述,混合属性和构造函数注入不是一个好主意。
为了提高 GuildUsersServiceImpl 的总体设计和可测试性,我建议坚持使用构造函数注入。这样您就可以: a) 将所有字段声明为 private final,因此一旦创建对象,它们将始终被设置,避免竞争条件和 NPE。 b) 在测试中使用适当的构造函数而不是 InjectMocks 来初始化 GuildUsersServiceImpl。
【讨论】:
【参考方案3】:把这个留给其他人,他们也浪费了一个下午试图让一个存储库返回他们配置的内容。最终我不得不做的是,我必须使用 @MockBean
注释来让 Mockito/Spring 组合正常工作,而不是使用 @Mock
注释存储库。
根据@MockBean
的javadoc:
当@MockBean 用于字段时,以及在应用程序上下文中注册时,mock 也会被注入到字段中。
【讨论】:
以上是关于尽管使用了when和thenReturn,但模拟存储库函数会导致空指针异常的主要内容,如果未能解决你的问题,请参考以下文章
Mockito 异常 - when() 需要一个参数,该参数必须是模拟上的方法调用
Mockito - JUnit + Mockito 单元测试之打桩 when().thenReturn()