首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JUNIT5 @InjectMocks抛出NullPointerException

JUNIT5 @InjectMocks抛出NullPointerException
EN

Stack Overflow用户
提问于 2020-05-10 10:02:49
回答 4查看 13.1K关注 0票数 4

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

我注意到在使用@InjectMocks注释时,当我有来自springboot的依赖项时,它们不会在测试阶段被注入。我将获得NullPointerException作为authenticationManager依赖项。但是,当服务方法只使用使用springboot JPA为实体类创建的存储库依赖时,测试就会通过。

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

代码语言:javascript
复制
@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

代码语言:javascript
复制
@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.");
}

请告诉我是否需要更多的细节从我的结尾。

EN

回答 4

Stack Overflow用户

发布于 2020-05-12 05:13:35

我不确定我是否100%跟随,正如我所看到的一样,您缺少一些注释。

如果要在单元测试中注入Spring,则需要@SpringBootTest/@ExtendWith(SpringExtension.class) (从我的观点来看,使其成为集成测试)。

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

并删除以下内容。

代码语言:javascript
复制
@BeforeEach
void init(){
    MockitoAnnotations.initMocks(this);
}

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

也许下面是的文章能有所帮助。

票数 10
EN

Stack Overflow用户

发布于 2020-06-08 12:40:29

TL/DR

为Spring依赖项添加缺少的模拟,如下所示:

代码语言:javascript
复制
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." );
    }
}

细目

在运行测试时,Spring不处于活动状态,除非您特别声明它使用的是注释。然而,良好的单元测试通常不需要Spring来运行。由于Spring不再管理我们的依赖项(这同样是好的),我们现在需要使用Mockito,手动地模拟和初始化它们,从而生成上面的单元测试套件。

因此,现在,每次被测试的类调用依赖项时,我们都会提供一个模拟。这将改变测试;我们不再测试“它是否生成预期值”,而是检查“它是否按顺序调用依赖项,正确地处理其结果”,这要抽象得多。

这就揭示了测试类的最大问题:过度耦合。它试图做很多很多事情,这使得我们的测试变得复杂和脆弱。减少这一点将提高生产和测试代码质量。

祝好运!

票数 3
EN

Stack Overflow用户

发布于 2021-09-30 09:50:14

在Junit 5中,只需使用@ExtendWith(SpringExtension.class)注释测试类以使用@InjectMocks,并确保您的导入应该来自import org.junit.jupiter.api.Test;,如下所示:

代码语言:javascript
复制
@ExtendWith(SpringExtension.class)
public class TestClass{
    @InjectMocks
    TestInjectMockClass testInjectMockClass;
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61710145

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档