TIL 23.01.26

쓰옹·2023년 1월 26일
0

개발자를 향해~~TIL✍

목록 보기
74/87

테스트코드 강의 들음.

// Error message
Unable to initialize @Spy annotated field 'jwtUtil'.
Please ensure that the type 'JwtUtil' has a no-arg constructor.
org.mockito.exceptions.base.MockitoException: Unable to initialize @Spy annotated field 'jwtUtil'.
Please ensure that the type 'JwtUtil' has a no-arg constructor.
@ExtendWith(MockitoExtension.class) // JUnit과 mockito연결
class UserServiceTest {

    ...

    @Spy
    private JwtUtil jwtUtil;

		...
}
==================================================================
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtil {

    private final UserDetailsServiceImpl userDetailsService;
		...
}

기본생성자라는걸 명확히 하라는 것 같지?
근데 그렇다고 JwtUtil에 기본생성자 생성 어노테이션을 붙이게 되면 error: constructor JwtUtil() is already defined in class JwtUtil 가 발생해서 아예 프로그램 작동이 안됨
jwtUtil이 UserDetailsService가 필요한거임 그럼 이것도 정의를 해줘야하는거임? 뭐 어쨌든 몰라서 튜터님께 여쭤봤더니 테스트모듈에 만들라고 하더라
테스트 모듈에 JwtUtil.java 클래스 만들었더니 잘 됨. 얘는 userDetailsService를 갖고있지 않음 걍 쌩 클래스임 그런데 이렇게 클래스를 만들었는데 로그인 테스트 할 때 jwtUtil.createToken() 메서드가 필요한데 테스트모듈에 만든 클래스에는 메서드가 포함되어있지 않아 오류가 발생함. 그래서 테스트용 메서드를 추가함

package com.sparta.blog.jwt;

import com.sparta.blog.entity.UserRoleEnum;

public class JwtUtil {
    public String createToken(String username, UserRoleEnum role) {
        return username;
    }

    public String refreshToken(String username, UserRoleEnum role) {
        return username;
    }
} 

그리고 테스트코드에서 /then에 username이 잘 들어오나 확인을 하게 되는건데..

assertThat(response.getAccessToken()).isEqualTo("test123");
assertThat(response.getRefreshToken()).isEqualTo("test123");

근데 @Spy가 원래 객체의 메서드를 그대로 쓰겠다는거 아닌가. 강의 내용 보면 @InjectMocks를 UserService에 달았으니까 main의 UserService의 객체인 JwtUtil의 메서드를 갖다가 써야하는거 아닌가아… 강의에선 그렇게 설명하는 것 같은데.. 그래서 @InjectMocks UserDetailsServiceImpl userDetailsService를 붙여주면 mail모듈의 jwtUtil을 사용하는데 key값에 null이 들어온다. 다른 분들이 찾은 원인은 JwtUtil.java에 붙어있는 @Value와 @PostConstruct가 안되서?라고 하던데 나는 아무리 구글링해도 모르겠다.. 그래서 테스트에서 @BeforeEach를 이용해서 테스트 메서드가 시작하기 전에 key값을 정의해주고 jwtUtil.init() 메서드를 실행시켜주더라. 근데 튜터님한테 이 문제를 갖고 처음에 jwtUtil이 실행이 안된다 했을 때 위에 방법과 아니면 jwtUtil을 mock객체로 만들어서 로그인 메서드 자체만 확인을 하고 jwtUtil에 대한 테스트는 해당 클래스의 테스트를 따로 만들어서 거기서 확인을 해야한다고 하심. 그렇게 마무리.

   @Test
    @DisplayName("로그인")
    void signin() {
        // given
        SigninRequestDto requestDto = SigninRequestDto.builder()
                .username("test123")
                .password("pw123")
                .build();

        User user = new User("test123", passwordEncoder.encode("pw123"), UserRoleEnum.USER);
        when(userRepository.findByUsername(any(String.class))).thenReturn(Optional.of(user));

        when(jwtUtil.createToken(user.getUsername(), user.getRole())).thenReturn("token");
        when(jwtUtil.refreshToken(user.getUsername(), user.getRole())).thenReturn("refreshToken");


        // when
        TokenResponseDto response = userService.signin(requestDto);

        // then 
        verify(jwtUtil, times(1)).createToken(any(String.class), any(UserRoleEnum.class));
        verify(jwtUtil, times(1)).refreshToken(any(String.class), any(UserRoleEnum.class));
   }

근데 저 when(jwtUtil..)은 안해도 된다는 생각이 든다 verify에서 검증을 하게 되니까..? 흠



Wrong password. 
// => UserService.java 의 signin 메소드 중 
//if (!passwordEncoder.matches(password, user.getPassword())) {
//            throw new IllegalArgumentException("Wrong password");
//} 여기의 예외처리 메세지임
java.lang.IllegalArgumentException: Wrong password
@ExtendWith(MockitoExtension.class) // JUnit과 mockito연결
class UserServiceTest {
	  ...

    @Spy
    private PasswordEncoder passwordEncoder;
		...

		@Test
    @DisplayName("로그인")
    void signin() {
        // given
        SigninRequestDto requestDto = SigninRequestDto.builder()
                .username("test123")
                .password("pw123")
                .build();
        User user = new User("test12", passwordEncoder.encode("pw123"), UserRoleEnum.USER);
				// user에 password가 null값이 들어감
        when(userRepository.findByUsername(any(String.class))).thenReturn(Optional.of(user));

        // when
        TokenResponseDto response = userService.signin(requestDto);

        // then
        assertThat(response.getAccessToken()).isEqualTo("test123");
        assertThat(response.getRefreshToken()).isEqualTo("test123");
    }
}
📎 @Spy A field annotated with @Spy can be initialized explicitly at declaration point. Alternatively, if you don't provide the instance Mockito will try to find zero argument constructor (even private) and create an instance for you. But **Mockito cannot instantiate inner classes, local classes, abstract classes and interfaces**

인터페이스는 인스턴스화할 수 없는데 PasswordEncoder 는 인터페이스이다. 그래서

`private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();` 수정함


검증할 때 형태검증과 행위검증이 있는데 형태검증은 assertThat으로 예상 값과 맞는지 확인을 하는 것이고 행위검증은 verify로 해당 로직이 시행이 되는지 확인하는 것이다.

profile
기록하자기록해!

0개의 댓글