userService test 중 회원가입은 잘되었으나 로그인때 에러발생함
Cannot invoke "com.example.myblog1.common.jwt.JwtUtil.createToken(java.lang.String, com.example.myblog1.user.entity.UserRoleEnum)" because "this.jwtUtil" is null
java.lang.NullPointerException: Cannot invoke "com.example.myblog1.common.jwt.JwtUtil.createToken(java.lang.String, com.example.myblog1.user.entity.UserRoleEnum)" because "this.jwtUtil" is null
@Slf4j
@Component
@NoArgsConstructor(force = true,access = AccessLevel.PROTECTED)
@RequiredArgsConstructor
public class JwtUtil {
......
@Value("${jwt.secret.key}")
private String secretKey;
.....
@PostConstruct
public void init() {
byte[] bytes = Base64.getDecoder().decode(secretKey);
key = Keys.hmacShaKeyFor(bytes);
}
@PostConstructor가 동작하지 않기 때문이다.
@PostConstructor가 동작하지 않기 때문에, 임의로 실행시켜야 함
@mock 객체들 선언이 끝난 쪽에 아래코드를 넣어주면 해결 됨
@BeforeEach
void prepare() {
ReflectionTestUtils.setField(jwtUtil,
"secretKey", // jwtUtil의 secretKey값이 저장될 변수
"7ZWt7ZW0OTntmZTsnbTtjIXtlZzqta3snYTrhIjrqLjshLjqs4TroZzrgpjslYTqsIDsnpDtm4zrpa3tlZzqsJzrsJzsnpDrpbzrp4zrk6TslrTqsIDsnpA="); // secretKey의 값
jwtUtil.init(); // jwtUtil에서 @PostConstructor가 동작하지 않기 때문에, 임의로 실행시켜야 함
}
userController Test 중 생긴 에러
Request processing failed; nested exception is org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
이 목객체로 생성한 리퀘스트와
content(new Gson().toJson(request)));
이 리퀘스트가 일치 하지 않는다는 말이다.
위의 📌 이 부분을 봐도 객체의주소값이 다르다고 말해주고 있다.
왜 객체의 주소값이 달려졌을까?
ResultActions resultActions = mockMvc.perform(
MockMvcRequestBuilders.post("/api/users/signup")
.contentType(MediaType.APPLICATION_JSON)
.content(new Gson().toJson(request)));
content(new Gson().toJson(request)));
이 소스 코드에서 new를 사용해서 새로운 인스턴스를 만들었기 때문에, 목킹할 때 사용한 아규먼트와 일치하지 않아서 발생할 에러이다.
어떤 타입의 postRequest 인스턴스를 받는지 상관없이 내가 미리 만들어둔 postRequest을 리턴하게 목킹하고 싶다면 any()를 사용하면 해결 됨
when(userService.signup(any(SignupRequest.class)))
.thenReturn(response);
완성코드
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Spy
private JwtUtil jwtUtil;
@BeforeEach
void prepare() {
ReflectionTestUtils.setField(jwtUtil,
"secretKey", // jwtUtil의 secretKey값이 저장될 변수
"7ZWt7ZW0OTntmZTsnbTtjIXtlZzqta3snYTrhIjrqLjshLjqs4TroZzrgpjslYTqsIDsnpDtm4zrpa3tlZzqsJzrsJzsnpDrpbzrp4zrk6TslrTqsIDsnpA="); // secretKey의 값
jwtUtil.init(); // jwtUtil에서 @PostConstructor가 동작하지 않기 때문에, 임의로 실행시켜야 함
}
@Spy
private BCryptPasswordEncoder passwordEncoder;
@Test
@DisplayName("로그인")
void login() { // 에러 왜 발생하는지 체크하기 -> 이유 : jwtUtil에서 @PostConstructor가 동작하지 않기 때문에, 임의로 실행시켜주니까 에러해결됨
//given
LoginRequest request = LoginRequest.builder()
.username("pororo")
.password("12341234")
.build();
MockHttpServletResponse servletResponse = new MockHttpServletResponse();//웹서버가 관장하는 값을 넣어줌
//목객체의 리턴값
User user = new User("pororo", passwordEncoder.encode("12341234"), "pororo@naver.com", UserRoleEnum.USER);
when(userRepository.findByUsername(any(String.class)))
.thenReturn(Optional.of(user));
//when
ResponseStatusDto response = userService.login(request, servletResponse);
//then
assertThat(response.getStatusCode()).isEqualTo(StatusEnum.LOGIN_SUCCESS.getStatusCode());
assertThat(response.getMsg()).isEqualTo(StatusEnum.LOGIN_SUCCESS.getMsg());
assertThat(servletResponse.getHeaderValue("Authorization").toString()).isNotEmpty();
verify(userRepository,times(1)).findByUsername(any(String.class));
}
}
mockMvc를 이용한 userController 단 테스트 코드 작성 후 런 하였더니 생긴 에러
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at com.example.myblog1.user.controller.UserControllerTest.signup_failed_pw(UserControllerTest.java:105)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Spring Boot에서 JUnit5와 이에 포함된 Mockito Core 3.x 버전을 사용할 때 아래와 같이 UnnecessaryStubbingException 에러가 나면서 테스트코드가 실패하는 경우가 있습니다.
1. -> at com.example.myblog1.user.controller.UserControllerTest.signup_failed_pw(UserControllerTest.java:105)
Please remove unnecessary stubbings or use 'lenient' strictness.
이는 mockito-core버전이 1.x일 때 없었던 Strictness(테스트코드의 엄격성)을 규정하기 위해 생긴 에러이며, mockito-core 2.x 버전에서 도입되었습니다.
불필요한 스터빙을 하지 않도록 되어있는데, 현재 코드 105줄에 쓰이지 않는 스터빙을 해놨기 때문에 저런 메시지가 보이는 것이고, lenient는 그런 제약을 느슨하게 허용하게 해줍니다.
doReturn, when 등의 앞에 lenient()를 추가해서 해당 stubbing이 미사용될 수 있음을 표시합니다.
lenient().when(userService.signup(request))
.thenReturn(response);
lenient는 그런 제약을 느슨하게 허용하게 함으로 에러 해결함
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Misplaced or misused argument matcher detected here:
-> at com.team.final8teamproject.contact.service.FaqServiceImplTest.lambda$getFaqList_throw$1(FaqServiceImplTest.java:149)
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(any());
verify(mock).someMethod(contains("foo"))
게시글이 없다면 예외처리 할 수 있는 테스트 코드를 짤때 발생함
@Test
@DisplayName("FAQ 조회_글이 없을때 예외발생")
void getFaqList_throw() {
//given
int page = 1;
int size = 10;
Direction direction = Direction.DESC;
String properties = "createdDate";
//lenient() Strictness(테스트코드의 엄격성) 느슨하게 해 줌
lenient().when(faqRepository.findAll(PageRequest.of(page - 1, size, direction, properties)))
.thenReturn(Page.empty());
//when&then
assertThrows(CustomException.class, () -> {
//faqServiceImpl.getFaqList(page - 1, size, direction, properties);
faqServiceImpl.getFaqList(any(),any(),any(),any());
});
}
faqServiceImpl.getFaqList(any(),any(),any(),any());
faqServiceImpl.getFaqList(any(),any(),any(),any());을
faqServiceImpl.getFaqList(page - 1, size, direction, properties);
해결