안녕하세요, Brad입니다. 오늘도 qna-atdd step1 미션을 진행하면서 공부한 내용을 정리해볼게요!
- 이 코드는 무엇을 의미하는 것일까요?
User user = new User("sanjigi", "password", "name", "javajigi@slipp.net");
when(userRepository.findByUserId(user.getUserId())).thenReturn(Optional.of(user));
분명 userRepository에 위와 같은 user가 없거든요. 그런데 어떻게 Repository에서 찾아서 테스트가 성공할 수 있었을까요?
이 when을 따라가보니 Mockito.java라는 클래스가 나타나네요. 모기랑 같은 발음같기도 하고. 근데 이 Mock이라는 것은 어디에선가 많이 들어본 것 같아요. 그럼 이 클래스를 알아봐야겠습니다. 알고보니 테스트클래스에 다음과 같은 클래스를 사용하는 것으로 명시를 해놓았네요.
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest extends BaseTest
Mock이라는 단어를 사전적 의미로 검색을 해보면 다음과 같습니다.
1.조롱하다 2.모의의 3.가짜의
즉, 가짜라는 것인데요. 현시점에서 이것을 사용하는 이유 및 활용에 대해 추측하자면 UserService를 테스트하려고 서버를 가동하고 데이터를 넣으면 너무나 비효율적이니 Mock을 사용하여 테스트 데이터 객체를 만들고 또 이것을 이용하여 userRepository에 넣어두어 UserService내 로직들이 제대로 작동하는 것인지 확인하기 위한 것이라고 추측할 수 있습니다. 실제로 테스트를 돌려보면 userRepository를 이용하는 것인데도 불구하고 웹서버를 가동시키지 않고 정말 빠르게 테스트 결과를 확인할 수 있었습니다.
실제로 stackoverflow에서 Mock에 대한 설명 중 다음과 같은 글을 볼 수 있었습니다.
Mocking is primarily used in unit testing. An object under test may have dependencies on other (complex) objects. To isolate the behavior of the object you want to replace the other objects by mocks that simulate the behavior of the real objects. This is useful if the real objects are impractical to incorporate into the unit test.
In short, mocking is creating objects that simulate the behavior of real objects.
출처 : https://stackoverflow.com/questions/2665812/what-is-mocking
결국 mock이라는 건 실제 객체를 시뮬레이터해보기 위해 만들어내는 객체인 것이네요!
Mokito Annotations(@Mock
, @InjectMocks
)를 사용하면 하나하나를 해석하면 다음과 같습니다.
@Mock
private UserRepository userRepository; // Create a mock for UserRepository
@InjectMocks
private UserService userService; // Inject the mocks as dependencies into userService
참고 : https://dzone.com/articles/spring-boot-unit-testing-and-mocking-with-mockito
UserRepository
를 가짜로 하나 만들어놓고 UserService
내 의존성을 주입시키는 것입니다. 실제 UserService
내에 UserRepository
가 사용되고 있죠. 그리고 위에서 본 when()
는 메서드 호출조건을, thenReturn()
은 그 조건을 충족할 때 리턴할 값을 지정합니다.
when(userRepository.findByUserId(user.getUserId())).thenReturn(Optional.of(user));
위 코드의 경우 userRepository.findByUserId(user.getUserId())
메서드가 호출되면 Optional.of(user)
를 return하라는 의미로 이해할 수 있습니다. Optional.of()
는 해당 매개변수의 값을 Optional
타입으로 만들어주는데요. userRepository.findByUserId(user.getUserId())
가 반환타입으로 Optional<User>
을 가지기 때문입니다.
when(userRepository.findByUserId("sanjigi")).thenReturn(Optional.empty());
userRepository내에 userId와 매치되는 user가 없을 때는 Optional.emtpy()
를 반환하는데요. 실제로 Optional타입에서 찾는 값이 없을 때 empty()
메서드를 사용한다고 하네요.
empty() Returns an empty Optional instance.
출처 : https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#empty--
- 로그인한 유저 구분을 테스트에서 어떻게 할까요?
public TestRestTemplate template() {
return template; // 로그인 안한 유저
}
public TestRestTemplate basicAuthTemplate(User loginUser) {
return template.withBasicAuth(loginUser.getUserId(), loginUser.getPassword()); // 로그인한 유저
}
테스트 코드 상에서 로그인한 유저와 안한 유저를 구분하기 위해서 안한 유저는 template()
메서드를 사용하고, 한 유저는 basicAuthTemplate()
메서드를 사용하는데요. 해당 클래스 내에 UserRepository
가 있더니 여기에서 지정한 유저를 가져와 세션유저값을 만드는 것 같네요. 참 신기하네요!
step1미션을 진행하다가 POST 테스트 때 중복되는 htmlForm을 만들어주는 Helper클래스를 추가하게 되었습니다. 제가 구현 한 것이 아닌 Pobi가 구현한 코드인데요. 와 정말 군더더기 없는 깔끔한 코드였고 내공이 느껴졌습니다! 간략하게 정리하면 다음과 같습니다.
- 중복되는 header부분은 static 메서드 내에서 만들고 return 값으로 해당 클래스 객체를 반환합니다 → 이를 통해 공통 헤더값을 넣고 따로 인스턴스 생성을 할 필요가 없어졌습니다
- 해당 클래스 내에 헤더 인스턴스 변수랑 파라미터 값들을 갖는 Map이 있는데요. 파라미터를 추가하는 메서드를 만들어 Map에 추가하고 리턴값으로 해당 객체를 반환합니다 → 객체를 반환함으로써 chain 형식으로 계속 파라미터를 추가할 수 있습니다.
- 마지막엔
build()
메서드를 통해HttpEntity
타입으로 헤더와 파라미터 값을 실어보냅니다.
오늘 정말 많은 것들을 배울 수 있었습니다. 그런데 아직 궁금증이 풀리지 않은 부분이 있습니다.
세션 확인 및 같은 유저 확인은 어디서 처리되고 있는 것일까요?
이 부분에 대해선 내일 수업시간에 여쭤봐야할 것 같네요!
'TIL' 카테고리의 다른 글
Today's Dev Notes2(2018-12-10) (0) | 2018.12.10 |
---|---|
Today's Dev Notes(2018-12-10) (0) | 2018.12.10 |
Today's Dev Notes(2018-12-08) (0) | 2018.12.08 |
Today's Dev Notes(2018-12-06) (0) | 2018.12.06 |
Today's Dev Notes(2018-12-03) (0) | 2018.12.03 |