JUNIT5 @InjectMocks抛出NullPointerException(空指针异常)。

问题描述 投票:0回答:1

这是我第一个使用TDD和JUNIT 5的项目。我的项目使用了最新的Springboot。

我注意到,当我有来自Springboot的依赖关系时,当使用@InjectMocks注解时,它们在测试阶段没有被注入。我得到了NullPointerException for authenticationManager dependency。然而,当服务方法只使用一个使用springboot JPA为实体类创建的仓库依赖时,测试就通过了。

下面是服务类和对应的测试类。UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {

@Autowired
AuthenticationManager authenticationManager;

@Autowired
UserRepository userRepository;

@Autowired
PasswordEncoder passwordEncoder;

@Autowired
UserDetailsService userDetailsService;

@Autowired
JWTUtil jwtUtil;

@Override
@Transactional
public AuthenticationResponseDTO login(AuthenticationRequestDTO authenticationRequestDTO) {
    try {
        authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(authenticationRequestDTO.getUserName(), authenticationRequestDTO.getPassword()));

        UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequestDTO.getUserName());

        return new AuthenticationResponseDTO(userDetails.getUsername(), jwtUtil.generateToken(userDetails));
    }
    catch (BadCredentialsException e) {
        throw e;
    }
}

UserServiceImplTest.java

@InjectMocks
private UserServiceImpl userServiceImpl;

@Mock
private UserRepository userRepository;

private User userMock;

private AuthenticationRequestDTO authenticationRequestDTO;

@BeforeEach
void init(){
    MockitoAnnotations.initMocks(this);
}

@BeforeEach
void setupUser(){
    userMock = new User();
    userMock.setUserName("sd");
    userMock.setPassword("sd");

    authenticationRequestDTO = new AuthenticationRequestDTO();
    authenticationRequestDTO.setUserName("sd");
    authenticationRequestDTO.setPassword("sd");
}

@Test
void testUserIsPresentOrNot(){

    Mockito.when( userRepository.findByUserName("sd") ).thenReturn(userMock);

    AuthenticationResponseDTO responseDTO = userServiceImpl.login(authenticationRequestDTO);

    assertNotNull(responseDTO);
    assertEquals(userMock.getUserName(), responseDTO.getName(), "user id should be same.");
}

如果需要我提供进一步的细节,请告诉我。

spring-boot spring-security tdd junit5
1个回答
1
投票

我不确定我是否100%听懂了,因为我可以看到你缺少一些注释。

如果你想在单元测试中注入Spring beans,你就需要 @SpringBootTest/@ExtendWith(SpringExtension.class) (从我的角度来看,使其成为集成测试)。

如果你想使用Mockito@InjectMocks@Mock 需要 @ExtendWith(MockitoExtension.class) 上面的测试类。

并删除下面的内容。

@BeforeEach
void init(){
    MockitoAnnotations.initMocks(this);
}

从我的角度来看,将依赖注入与spring和Mockito混合在一起会太复杂。我宁愿只用Mockito来进行单元测试。

也许 下面 文章可以帮助。


0
投票

TLDR

为Spring依赖关系添加缺失的mocks,像这样。

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.when;

class UserServiceImplSuite
{
    public static final String USER_NAME = "USER_NAME";
    public static final String PASSWORD = "PASSWORD";
    @InjectMocks
    private UserServiceImpl userServiceImpl;

    @Mock
    private UserRepository userRepository;

    @Mock
    private AuthenticationManager authenticationManager;

    @Mock
    private UserDetailsService userDetailsService;

    @Mock
    private JWTUtil jwtUtil;

    private User userMock;

    private UserDetails userDetailsMock;

    private AuthenticationRequestDTO authenticationRequestDTO;

    @BeforeEach
    void init()
    {
        MockitoAnnotations.initMocks( this );
    }

    @BeforeEach
    void setupUser()
    {
        userMock = new User();
        userMock.setUserName( USER_NAME );
        userMock.setPassword( PASSWORD );

        userDetailsMock = new UserDetails();
        userDetailsMock.setUsername( USER_NAME );
        userDetailsMock.setPassword( PASSWORD );

        authenticationRequestDTO = new AuthenticationRequestDTO();
        authenticationRequestDTO.setUserName( USER_NAME );
        authenticationRequestDTO.setPassword( PASSWORD );
    }

    @Test
    void testUserIsPresentOrNot()
    {

        when( userRepository.findByUserName( USER_NAME ) ).thenReturn( userMock );
        when( userDetailsService.loadUserByUsername( USER_NAME ) )
                .thenReturn( userDetailsMock );
        when( jwtUtil.generateToken( userDetailsMock ) )
                .thenReturn( PASSWORD );

        AuthenticationResponseDTO responseDTO = userServiceImpl.login( authenticationRequestDTO );

        assertNotNull( responseDTO );
        assertEquals( userMock.getUserName(), responseDTO.getName(), "user id should be same." );
    }
}

Breakdown

在运行测试的时候,Spring是不活跃的,除非你用注释特别声明它是活跃的。然而,好的单元测试一般不需要Spring来运行.由于Spring不再管理我们的依赖关系(这又是好事),我们现在需要使用Mockito来接管;手动嘲弄和初始化它们,产生上面的单元测试套件。

所以现在,每次测试类调用依赖关系时,我们都会提供一个mock。这改变了测试;我们不再测试 "是否产生了预期的值",而是检查 "是否按顺序调用了依赖关系,正确处理了它们的结果",这更加抽象。

这就暴露了测试类最大的问题:过度耦合。它试图做很多很多事情,这使得我们的测试变得复杂而脆弱。减少这个问题将改善生产和测试代码的质量。

祝大家好运

© www.soinside.com 2019 - 2024. All rights reserved.