인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

river님의 프로필 이미지
river

작성한 질문수

스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

회원 서비스 테스트

같은 리파지토리 인스턴스를 사용하도록 변경하려면.. (+테스트 관련)

해결된 질문

작성

·

289

0

[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예

[질문 내용]
안녕하세요 선생님!
좋은 강의 잘 듣고 있습니다.
 
회원서비스테스트 강의
13:10 ~ 이후 내용에 관해서 질문이 있습니다.
 
서비스와 리파지토리에서 같은 리파지토리 인스턴스를 사용하려면
아래의 코드처럼 변경해야 한다고 이해했는데요
 
같은 리파지토리 인스턴스를 사용하기 위함이라고 하셨는데,
왜 아래 코드처럼 사용하지 않고 beforeEach에서 계속 새로운 리파지토리를 생성해서 주입하는 건지 궁금합니다.
 
추가로,
테스트는 독립적으로 실행 돼야하기 때문에 각각 생성해주도록 코드를 변경했다고 하셨는데,
테스트는 독립적으로 실행 돼야한다는 부분이 이해가 잘 안됩니다.
독립적이라는 것은 여러 테스트가 참조하는 각각 인스턴스들이 모두 달라야 하는 건가요?
예를들어..
`회원가입` 테스트와 `중복_회원_예외` 테스트에서 참조 중인
MemberService 들의 인스턴스 주소가 테스트 별로 달라야 하는 건가요?
그리고 왜 독립적으로 실행 돼야하는 건지도 궁금합니다.
 
테스트 파일 전체 테스트를 실행하면, 각각 독립적으로 실행되는 것처럼 보이는데..
제가 수정한 코드처럼 같은 리파지토리 인스턴스를 사용하면
테스트가 독립적이지 않게 되는 건가요?

답변 2

3

안녕하세요. bluuminn님, 공식 서포터즈 OMG입니다.

 

제 답변이 많이 미흡했네요.. 영한님의 설명을 확인하고 답변을 드렸어야 했는데 혼선을 드린것 같습니다.죄송합니다

강의를 바탕으로 다시 답변 드리겠습니다. 

영한님께서 말씀하신 13분 10초 경 위의 코드를 설명하시면서 애매한 부분이 있다고 설명하셨습니다.

그 이유로는 

MemberServiceTest테스트에서도 memberRepository = new Memory~();

로 Repository를 생성하고, MemberService에서도 생성해서 동일한 타입의 인스턴스를 두번 생성하는 것 때문에 이러한 설명을 하셨습니다.

이것을 해결하기 위해, MemberService에서는 직접 new로 생성하지 않습니다. 대신 생성자에서 값을 주입 받도록 코드를 수정하였습니다.

이렇게 코드를 바꾸면 MemberService에서 new로 생성하는 것이 아닌 MemberService 코드를 사용하는 쪽에서 Repository를 파라미터로 넣어서 생성이 이루어집니다.

이 것을 코드로 옮기면 다음과 같습니다.

MemberService를 사용하는 코드에서, Repository를 생성하고, 생성된 Repository를 MemberService에 파라미터로 넘겨서 인스턴스를 생성합니다.

 

질문으로 돌아와서

"왜 위와 같이 작성하지 않았는지?"에 대한 답변은 MemoryMemberRepository를 필드에서 생성해주고, @BeforeEach에서 초기화를 진행하지 않을 경우, 

현재 강의 코드에서는 코드가 구현되어 있지 않지만,  테스트하려는 테스트 코드가 아닌 다른 어떠한 테스트에서 repository를 바꿔치기 할 수 있고, 이러한 경우 repository가 바뀔 수 있는 여지가 있습니다. 즉, 다른 테스트로 인한 영향이 발생할 가능성이란 것이죠.

그렇기 때문에 @BeforeEach를 통해 동일한 repository에서 초기 세팅으로의 테스트가 진행되어야 합니다. 

 

public class MemberServiceTest {
MemberService service;
MemoryMemberRepository repository = new MemoryMemberRepository();

@BeforeEach
void beforeEach() {
service = new MemberService(repository);
}
@Test
void changeRepository() {
repository = new H2MemoryMemberRepository();
/// ...
}
}

 

 

위의 코드가 억지스러운 면도 있지만 의도 자체로만 놓고 본다면 맞다고 생각합니다. 테스트에서 다른 테스트에 영향을 미칠 가능성이 있는 상황을 만들지 않기 위해 @BeforeEach와 @AfterEach로 각각 할당과 해제 작업이 이루어 진다고 보시면 될 것 같습니다.

 

 

감사합니다.

이렇게 함으로써 각 테스트는 인스턴스의 생성(@BeforeEach)과 테스트 환경의 초기화(@AfterEach)의 작업이 테스트 실행 전/후에 걸쳐 진행이 되어 각 테스트는 서로 영향을 끼치지 않는 독립적인 테스트가 가능해집니다.

 

혼선을 드린것 같아 죄송하네요.. 이전 설명 대신 위에서 설명드린 내용으로 이해해주세요.

 

river님의 프로필 이미지
river
질문자

자세한 답변 감사합니다.

덕분에 이해됐습니다!

좋은 하루 보내세요~!

1

안녕하세요. bluuminn님, 공식 서포터즈 OMG입니다.

테스트가 독립적으로 실행되어야 한다는 것은 가령 MemberServiceTest에는 2개의 테스트가 있다고 가정하고 설명드리겠습니다.

1. @Test 회원가입 2. @Test 중복검사 1번 테스트에서 이름이 홍길동인 회원을 가입하였을 때, 2번에서 중복검사를 하기 위해 홍길동의 회원을 가입시키는 과정을 한번 진행 한다고 하면 1번에서 가입한 홍길동의 정보는 2번에 영향을 미치면 안된다는 뜻입니다.

2번 테스트에서 홍길동의 회원가입을 진행하였다면 홍길동의 정보는 없어야 되는 정보인데 1번에서 홍길동을 회원가입 시켰을 때의 정보가 2번의 테스트에 영향을 미친다면 이것은 테스트가 독립적으로 실행되지 않는다는 것을 뜻합니다. 위 내용과 더불어 bluuminn님이 생각하신대로 @BeforeEach에서 인스턴스를 생성할 경우 매 테스트 마다 MemberRepository가 생성됩니다.

 

하지만 강의에서 처럼 필드영역에서 한번 생성할 경우, 테스트가 종료되면 @AfterEach를 통해 리포지토리가 비워지고 처음 생성한 MemberRepsitory로 서로 독립적인 테스트가 가능하며 객체 생성도 딱 한번만 발생하게 됩니다.

 

감사합니다.

river님의 프로필 이미지
river
질문자

답변 감사합니다!

강의에서처럼 필드영역에 한번 선언한다는 부분을 못찾겠는데 혹시 어디 부분을 봐야하는지 알려주실 수 있나요?

질문의 두번째 사진은 제가 수정한 코드입니다!

river님의 프로필 이미지
river

작성한 질문수

질문하기