这是我第一个使用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.");
}
如果需要我提供进一步的细节,请告诉我。
我不确定我是否100%听懂了,因为我可以看到你缺少一些注释。
如果你想在单元测试中注入Spring beans,你就需要 @SpringBootTest/@ExtendWith(SpringExtension.class)
(从我的角度来看,使其成为集成测试)。
如果你想使用Mockito@InjectMocks
和 @Mock
需要 @ExtendWith(MockitoExtension.class)
上面的测试类。
并删除下面的内容。
@BeforeEach
void init(){
MockitoAnnotations.initMocks(this);
}
从我的角度来看,将依赖注入与spring和Mockito混合在一起会太复杂。我宁愿只用Mockito来进行单元测试。
也许 下面 文章可以帮助。
为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." );
}
}
在运行测试的时候,Spring是不活跃的,除非你用注释特别声明它是活跃的。然而,好的单元测试一般不需要Spring来运行.由于Spring不再管理我们的依赖关系(这又是好事),我们现在需要使用Mockito来接管;手动嘲弄和初始化它们,产生上面的单元测试套件。
所以现在,每次测试类调用依赖关系时,我们都会提供一个mock。这改变了测试;我们不再测试 "是否产生了预期的值",而是检查 "是否按顺序调用了依赖关系,正确处理了它们的结果",这更加抽象。
这就暴露了测试类最大的问题:过度耦合。它试图做很多很多事情,这使得我们的测试变得复杂而脆弱。减少这个问题将改善生产和测试代码的质量。
祝大家好运