嵌套在测试方法中的方法字段上的 NullPointerException
NullPointerException on a field of method nested in tested method
我正在尝试使用 Junit5 和 mockito 编写单元测试,但是当被测试的方法调用嵌套在自身中的另一个方法时,我得到了 NullPointerException。在调试时,我在该方法参数上得到“找不到局部变量”。
这是代码:
测试class:
@ExtendWith(MockitoExtension.class)
class DeckServiceImplTest {
@Mock
private UserServiceImpl userService;
@Mock
private DeckRepository deckRepository;
@InjectMocks
private DeckServiceImpl deckService;
@BeforeEach
void setUp() {
deckService = new DeckServiceImpl(deckRepository, userService);
}
@Test
@DisplayName("when deck id is provided returned deck should be correct")
@WithMockUser(username = "user", password = "user", roles = "USER")
public void whenDeckIdIsProvidedThenRetrievedDeckIsCorrect() {
//given
DeckDto testDeck = null;
User deckBaseEntityOwner = new User();
deckBaseEntityOwner.setEmail("testemail@test.com");
deckBaseEntityOwner.setId(10L);
deckBaseEntityOwner.setRole(Role.ADMIN);
Deck deckBaseEntity = new Deck();
deckBaseEntity.setName("deck name");
deckBaseEntity.setAccessLevel(AccessLevel.PUBLIC.getAccessLevel());
deckBaseEntity.setOwner(deckBaseEntityOwner);
deckBaseEntity.setId(1L);
//when
when(deckRepository.findById(anyLong())).thenReturn(of(deckBaseEntity));
testDeck = deckService.findById(1L);
//then
verify(deckRepository).findById(anyLong());
assertNotNull(testDeck);
assertEquals("deck name", testDeck.getName());
assertEquals(10L, testDeck.getOwnerId());
assertEquals("testemail@test.com", testDeck.getOwnerEmail());
assertEquals(AccessLevel.PUBLIC.getAccessLevel(), testDeck.getAccessLevel());
}
}
已测试的服务方式:
@Override
public DeckDto findById(Long id) throws ElementNotFoundByIdException, PermissionDeniedException {
Deck deck = deckRepository.findById(id).orElseThrow(() -> new ElementNotFoundByIdException(
CAN_NOT_FIND_DECK_BY_ID_ERROR_MESSAGE.getMessage() + id,
CAN_NOT_FIND_DECK_BY_ID_ERROR_CODE.getValue()
));
validatePermissionByDeckAccessLevel(deck);
return modelMapper.map(deck, DeckDto.class);
}
private void validatePermissionByDeckAccessLevel(Deck deck) throws PermissionDeniedException {
ArrayList<? extends GrantedAuthority> authorities = new ArrayList<>(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
if (!SecurityContextHolder.getContext().getAuthentication().getName().equals(deck.getOwner().getEmail())
&& !deck.getAccessLevel().equals(AccessLevel.PUBLIC.getAccessLevel())
&& !authorities.get(0).getAuthority().equals(Role.ADMIN.getRole())) {
throw new PermissionDeniedException(
USER_DONT_HAVE_PERMISSIONS_ERROR_MESSAGE.getMessage(),
USER_DONT_HAVE_PERMISSIONS_ERROR_CODE.getValue()
);
}
}
每次它进入 validatePermissionByDeckAccessLevel(Deck deck) 方法时,它都会在 deck 字段上抛出 NPE,即使我确定(我仔细检查过)我正在传递非空值。在尝试调试时,我收到 screen.
上显示的消息
validatePermissionByDeckAccessLevel(Deck deck) 所做的只是检查角色或电子邮件(不过应该无关紧要,因为它已经过测试并且可以正常工作)。
你的 deck
没有问题,你已经用 AccessLevel
和 Owner
存根甲板,而所有者已经有 email
.
对此:
While debugging im getting "cannot find local variable" on that method parameter
应该没有关系,你可能只是在变量还不存在的地方放了一个错误的断点。
问题应该来自以下行之一:
SecurityContextHolder.getContext().getAuthentication().getAuthorities()
SecurityContextHolder.getContext().getAuthentication().getName()
authorities.get(0).getAuthority()
除了上述主要内容外,您还可以在代码中进行改进:
- 您已经使用了
@InjectMocks
,所以这一行是多余的:deckService = new DeckServiceImpl(deckRepository, userService);
- 你用了实现,虽然没有问题,但是还是用接口比较好:
private UserServiceImpl userService;
- 您应该通过 api 进行测试,而不是执行:
private DeckServiceImpl deckService;
DeckDto testDeck = null;
这行是不必要的,你可以这样做: DeckDto testDeck = deckService.findById(1L);
- 避免使用
anyLong()
- 你应该知道你要测试什么,所以你应该准确地测试你想测试的行为。
- 你调用了两次
SecurityContextHolder.getContext().getAuthentication()
,最好为其声明一个变量,然后使用getAuthorities
或getName
- 这一行:(使用
List
而不是ArrayList
声明变量)
ArrayList<? extends GrantedAuthority> authorities = new ArrayList<>(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
可改为:
List<? extends GrantedAuthority> authorities = new ArrayList<>(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
我正在尝试使用 Junit5 和 mockito 编写单元测试,但是当被测试的方法调用嵌套在自身中的另一个方法时,我得到了 NullPointerException。在调试时,我在该方法参数上得到“找不到局部变量”。 这是代码:
测试class:
@ExtendWith(MockitoExtension.class)
class DeckServiceImplTest {
@Mock
private UserServiceImpl userService;
@Mock
private DeckRepository deckRepository;
@InjectMocks
private DeckServiceImpl deckService;
@BeforeEach
void setUp() {
deckService = new DeckServiceImpl(deckRepository, userService);
}
@Test
@DisplayName("when deck id is provided returned deck should be correct")
@WithMockUser(username = "user", password = "user", roles = "USER")
public void whenDeckIdIsProvidedThenRetrievedDeckIsCorrect() {
//given
DeckDto testDeck = null;
User deckBaseEntityOwner = new User();
deckBaseEntityOwner.setEmail("testemail@test.com");
deckBaseEntityOwner.setId(10L);
deckBaseEntityOwner.setRole(Role.ADMIN);
Deck deckBaseEntity = new Deck();
deckBaseEntity.setName("deck name");
deckBaseEntity.setAccessLevel(AccessLevel.PUBLIC.getAccessLevel());
deckBaseEntity.setOwner(deckBaseEntityOwner);
deckBaseEntity.setId(1L);
//when
when(deckRepository.findById(anyLong())).thenReturn(of(deckBaseEntity));
testDeck = deckService.findById(1L);
//then
verify(deckRepository).findById(anyLong());
assertNotNull(testDeck);
assertEquals("deck name", testDeck.getName());
assertEquals(10L, testDeck.getOwnerId());
assertEquals("testemail@test.com", testDeck.getOwnerEmail());
assertEquals(AccessLevel.PUBLIC.getAccessLevel(), testDeck.getAccessLevel());
}
}
已测试的服务方式:
@Override
public DeckDto findById(Long id) throws ElementNotFoundByIdException, PermissionDeniedException {
Deck deck = deckRepository.findById(id).orElseThrow(() -> new ElementNotFoundByIdException(
CAN_NOT_FIND_DECK_BY_ID_ERROR_MESSAGE.getMessage() + id,
CAN_NOT_FIND_DECK_BY_ID_ERROR_CODE.getValue()
));
validatePermissionByDeckAccessLevel(deck);
return modelMapper.map(deck, DeckDto.class);
}
private void validatePermissionByDeckAccessLevel(Deck deck) throws PermissionDeniedException {
ArrayList<? extends GrantedAuthority> authorities = new ArrayList<>(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
if (!SecurityContextHolder.getContext().getAuthentication().getName().equals(deck.getOwner().getEmail())
&& !deck.getAccessLevel().equals(AccessLevel.PUBLIC.getAccessLevel())
&& !authorities.get(0).getAuthority().equals(Role.ADMIN.getRole())) {
throw new PermissionDeniedException(
USER_DONT_HAVE_PERMISSIONS_ERROR_MESSAGE.getMessage(),
USER_DONT_HAVE_PERMISSIONS_ERROR_CODE.getValue()
);
}
}
每次它进入 validatePermissionByDeckAccessLevel(Deck deck) 方法时,它都会在 deck 字段上抛出 NPE,即使我确定(我仔细检查过)我正在传递非空值。在尝试调试时,我收到 screen.
上显示的消息validatePermissionByDeckAccessLevel(Deck deck) 所做的只是检查角色或电子邮件(不过应该无关紧要,因为它已经过测试并且可以正常工作)。
你的 deck
没有问题,你已经用 AccessLevel
和 Owner
存根甲板,而所有者已经有 email
.
对此:
While debugging im getting "cannot find local variable" on that method parameter
应该没有关系,你可能只是在变量还不存在的地方放了一个错误的断点。
问题应该来自以下行之一:
SecurityContextHolder.getContext().getAuthentication().getAuthorities()
SecurityContextHolder.getContext().getAuthentication().getName()
authorities.get(0).getAuthority()
除了上述主要内容外,您还可以在代码中进行改进:
- 您已经使用了
@InjectMocks
,所以这一行是多余的:deckService = new DeckServiceImpl(deckRepository, userService);
- 你用了实现,虽然没有问题,但是还是用接口比较好:
private UserServiceImpl userService;
- 您应该通过 api 进行测试,而不是执行:
private DeckServiceImpl deckService;
DeckDto testDeck = null;
这行是不必要的,你可以这样做:DeckDto testDeck = deckService.findById(1L);
- 避免使用
anyLong()
- 你应该知道你要测试什么,所以你应该准确地测试你想测试的行为。 - 你调用了两次
SecurityContextHolder.getContext().getAuthentication()
,最好为其声明一个变量,然后使用getAuthorities
或getName
- 这一行:(使用
List
而不是ArrayList
声明变量)
ArrayList<? extends GrantedAuthority> authorities = new ArrayList<>(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
可改为:
List<? extends GrantedAuthority> authorities = new ArrayList<>(SecurityContextHolder.getContext().getAuthentication().getAuthorities());