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

ghddmsrl100님의 프로필 이미지
ghddmsrl100

작성한 질문수

스프링 핵심 원리 - 기본편

생성자 주입을 선택해라!

스프링 부트를 쓰지 않는 단위 테스트의 모범 사례가 궁금합니다.

작성

·

450

·

수정됨

1

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)

질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오) 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예

[질문 내용]
안녕하세요. 김영한님의 강의를 들으며 스프링 부트를 활용한 웹 어플리케이션 개발을 진행하고 있는데, 단위 테스트 코드 작성 시 모범 사례(?)가 있는지 궁금해 질문하게 되었습니다. 영한님의 강의에서 "좋은 테스트는 단위 테스트부터 출발하며, 단위 테스트를 잘 만드는 것이 좋은 테스트의 출발이다."라는 의미의 말을 기억하며 단위 테스트 코드를 다음과 같이 작성해보았습니다. 

AppConfig 코드

@Configuration 
public class AppConfig {
 @Bean public MemberService memberService() {
   return new MemberServiceImpl(memberRepository()); 
 } 
 @Bean public MemberRepository memberRepository() {
   return new MemoryMemberRepository(); 
 } // ... 
}  

 

단위 테스트 대상 (서비스 컴포넌트)

@Component public class MemberServiceImpl implements MemberService{ 
  private final MemberRepository memberRepository; 
  @Autowired public MemberServiceImpl(MemberRepository memberRepository) { 
    this.memberRepository = memberRepository; 
  } // ... 
}

 

위 서비스 컴포넌트를 단위 테스트 하기 위해 아래와 같은 테스트 코드를 작성했습니다.

class MemberServiceImplTest {
 @Test @DisplayName("사례1") void case1() {
   // @SpringBootTest를 사용하진 않지만 ApplicationContext를 사용 
   ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); 
   // Bean 가져오기 
   MemberService memberService = ac.getBean(MemberService.class);
   MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class); // 가져온 것 확인하기
   Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
   Assertions.assertThat(memberRepository).isInstanceOf(MemberRepository.class);
 }
 @Test @DisplayName("사례2") void case2() {
   // 순수 자바로 가져오기
   MemberRepository memberRepository = new MemoryMemberRepository();
   MemberService memberService = new MemberServiceImpl(memberRepository);
   // 멤버 서비스 테스트 세팅
   Member newMember = new Member(1L, "Test", Grade.VIP);
   memberService.join(newMember);
   // 멤버 서비스 테스트 검증
   Member findMember = memberService.findMember(1L);
   Assertions.assertThat(newMember.getId()).isEqualTo(findMember.getId());
 } 
}//

 

두 테스트 모두 테스트를 통과합니다.제가 궁금한 것은

  1. 사례1은 과연 유닛 테스트라고 할 수 있을까요? @SpringBootTest를 붙여주지 않았을 뿐이지 스프링 컨테이너를 사용하기 때문에 유닛 테스트가 아닌 스프링 부트를 사용하는 통합 테스트라고 봐야하는거 아닌지 궁금합니다.

  2. 사례2는 순수하게 자바로 인스턴스를 생성해 테스트를 진행합니다. 따라서 완전히 스프링에서 독립된 유닛 테스트라고 볼 수 있을 것 같습니다. 그런데 이 경우, 스프링의 도움을 받지 않기 때문에 의존성 주입을 직접 해줘야 한다는 점이 걸립니다. 따라서 사례2 처럼 직접 인스턴스를 선언하여 생성자로 주입해주는 것이 좋은 코드인지 의문이 듭니다.

  3. MemberServiceImpl 코드를 보면 생성자에 자동 의존 주입이 명시되어있는데, 만약 사례2처럼 생성할때 직접 수동으로 삽입한다면 @Autowired는 무시되는 것인가요?

 

답변 2

1

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. ghddmsrl100님

1. 사례 1: 유닛 테스트 vs 통합 테스트

- 사례 1에서는 ApplicationContext를 사용하여 스프링의 설정을 로드합니다. 이는 전형적인 단위 테스트보다는 통합 테스트에 가깝습니다. 단위 테스트는 일반적으로 외부 의존성(여기서는 스프링 컨텍스트) 없이 클래스나 메소드의 기능만을 독립적으로 테스트하는 것을 목표로 합니다.

- 스프링 컨테이너를 사용하는 테스트는, 실제 애플리케이션의 동작 환경을 더욱 잘 반영하나, 테스트의 실행 속도가 느려지고 복잡성이 증가할 수 있습니다. 이 경우, 유닛 테스트보다는 스프링 기반의 통합 테스트로 보는 것이 적절합니다.

2. 사례 2: 순수 자바 기반 유닛 테스트

- 사례 2에서는 순수 자바 코드를 사용하여 직접 인스턴스를 생성하고, 의존성을 주입하는 방식을 사용합니다. 이는 전형적인 유닛 테스트의 예로 볼 수 있습니다.

- 이 방식은 스프링 컨테이너에 의존하지 않기 때문에 테스트가 빠르고 가볍습니다. 그러나 의존성을 수동으로 관리해야 하는 번거로움이 있습니다.

- 스프링의 @Autowired와 같은 의존성 주입 어노테이션은 스프링 컨테이너에서만 작동합니다. 순수 자바 테스트에서는 이러한 어노테이션이 무시되며, 개발자가 직접 필요한 의존성을 주입해야 합니다.

3. `@Autowired`의 작동 방식

- @Autowired는 스프링이 관리하는 빈(bean)에 대해 의존성을 자동으로 주입하는 데 사용됩니다.

- 순수 자바 테스트(사례 2)에서는 스프링 컨테이너가 관여하지 않으므로 @Autowired는 기능하지 않습니다. 이 경우, 생성자를 통한 수동 주입이 필요합니다.

종합하자면, 사례 1은 스프링 기반의 통합 테스트에 가깝고, 사례 2는 순수 자바 기반의 유닛 테스트에 해당합니다. 둘 사이에는 트레이드오프가 있으며, 프로젝트의 요구 사항과 테스트의 목적에 따라 적절한 접근 방식을 선택해야 합니다.

여러가지 방식의 테스트가 필요하지만, 각각의 단위를 단순하고 빠르고 쉽게 테스트할 수 있는 순수 자바 기반 유닛 테스트를 지향하는 것이 좋습니다.

감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다.

해당 강의와 비슷한 주제로 다른 수강생 분이 질문하신 게시물을 찾았습니다. 이 게시물은 강의 중 Repository 테스트를 어떻게 진행하는 것이 좋은지에 대한 궁금증을 담고 있으며, 강사님이 직접 답변을 해주셨습니다. 답변에 따르면, 리포지토리는 실제 데이터베이스까지 실행되어야 정확하게 테스트 결과를 알 수 있는 경우가 많기 때문에 인메모리 데이터베이스를 사용하여 통합 테스트하는 것이 좋다고 합니다.

답변과 관련된 게시글을 참고하시면 도움이 될 것 같습니다.

또한, 스프링 컨테이너를 사용하지 않고 순수 자바로만 단위 테스트를 진행하는 방법에 대한 다른 질문도 참고하실 수 있습니다.

이 게시물들이 귀하의 질문에 대한 해답을 찾는 데 도움이 되길 바랍니다.

ghddmsrl100님의 프로필 이미지
ghddmsrl100

작성한 질문수

질문하기