Java 미드레벨 테스트·코드품질 기술면접

Java 미드레벨 (3~7년) 테스트 · 코드품질 5문항 조회수 20 · 2026-08-17 (월) 22:11:09
1 단위 테스트 설계
Medium

Q. JUnit5에서 @ParameterizedTest를 사용하는 이유와 장점을 설명하고, @ValueSource, @CsvSource, @MethodSource의 차이점과 각각 어떤 상황에서 사용하는 것이 적절한지 설명해주세요.

동일한 테스트 로직을 여러 입력값에 대해 반복 실행할 때의 이점을 생각해보세요.

A. 모범답안

@ParameterizedTest는 동일한 테스트 로직을 다양한 입력값에 대해 반복 실행하여 테스트 코드의 중복을 제거하고 가독성을 높입니다. @ValueSource는 단일 파라미터의 간단한 값들을 전달할 때 사용하며, 문자열이나 숫자 배열을 직접 명시합니다. @CsvSource는 여러 파라미터를 조합하여 전달할 때 사용하며, CSV 형식으로 테스트 케이스를 명시적으로 작성합니다. @MethodSource는 복잡한 객체나 동적으로 생성되는 테스트 데이터가 필요할 때 사용하며, 별도 메서드에서 Stream이나 Collection을 반환합니다. 실무에서는 경계값 테스트나 다양한 입력 조합 검증 시 테스트 커버리지를 높이면서도 코드 중복을 최소화할 수 있습니다.

핵심 포인트
  • • @ParameterizedTest로 테스트 코드 중복 제거
  • • @ValueSource는 단일 파라미터 간단한 값
  • • @CsvSource는 여러 파라미터 조합
  • • @MethodSource는 복잡한 객체나 동적 데이터
답변에 넣으면 좋은 키워드
ParameterizedTest ValueSource CsvSource MethodSource 테스트 중복 제거 경계값 테스트
실무에서는

입력값 검증 로직에서 다양한 유효/무효 케이스를 효율적으로 테스트할 때 사용합니다.

Follow-up 질문

@MethodSource에서 static 메서드를 사용해야 하는 이유와, 테스트 인스턴스 라이프사이클과의 관계를 설명해주세요.

2 Mocking 전략
Medium

Q. Mockito에서 Mock, Spy, Stub의 차이점을 설명하고, 각각 어떤 상황에서 사용해야 하는지 설명해주세요. 또한 @InjectMocks를 사용할 때 주의해야 할 점도 함께 설명해주세요.

실제 객체의 동작을 얼마나 활용하느냐에 따라 선택이 달라집니다.

A. 모범답안

Mock은 모든 메서드가 기본값을 반환하는 가짜 객체로, 외부 의존성을 완전히 격리하여 테스트할 때 사용합니다. Spy는 실제 객체를 감싸서 일부 메서드만 stubbing하고 나머지는 실제 동작을 수행하므로, 레거시 코드나 부분적인 모킹이 필요할 때 사용합니다. Stub은 미리 정의된 응답을 반환하도록 설정한 객체로, when-thenReturn 구문으로 특정 동작을 정의합니다. @InjectMocks는 필드 주입, 생성자 주입, setter 주입 순서로 시도하는데, 생성자 주입을 명시적으로 사용하는 것이 안전하며, final 필드나 복잡한 의존성 구조에서는 수동으로 객체를 생성하는 것이 더 명확합니다. 실무에서는 Mock을 기본으로 사용하고, 부분 모킹이 필요한 특수한 경우에만 Spy를 제한적으로 사용합니다.

핵심 포인트
  • • Mock은 완전한 가짜 객체, 외부 의존성 격리
  • • Spy는 실제 객체 기반 부분 모킹
  • • Stub은 미리 정의된 응답 반환
  • • @InjectMocks는 생성자 주입 방식 권장
답변에 넣으면 좋은 키워드
Mock Spy Stub Mockito InjectMocks 의존성 격리
실무에서는

외부 API, 데이터베이스, 메시징 시스템 등 외부 의존성을 격리하여 단위 테스트를 작성할 때 사용합니다.

Follow-up 질문

verify() 메서드를 과도하게 사용하면 테스트가 구현에 강하게 결합되는 문제가 발생합니다. 이를 피하기 위한 테스트 작성 전략은 무엇인가요?

3 테스트 커버리지
Medium

Q. 테스트 커버리지(Code Coverage)의 종류인 Statement Coverage, Branch Coverage, Path Coverage의 차이점을 설명하고, 높은 커버리지가 반드시 좋은 테스트를 의미하지 않는 이유를 설명해주세요. 실무에서 적절한 커버리지 목표는 어떻게 설정해야 하나요?

커버리지는 코드가 실행되었는지를 측정하지, 올바르게 동작하는지를 보장하지는 않습니다.

A. 모범답안

Statement Coverage는 전체 코드 라인 중 실행된 비율을 측정하며 가장 기본적인 지표입니다. Branch Coverage는 조건문의 모든 분기(true/false)가 실행되었는지를 측정하여 더 정교한 테스트를 보장합니다. Path Coverage는 가능한 모든 실행 경로를 측정하지만 복잡도가 높아 현실적으로 100% 달성이 어렵습니다. 높은 커버리지가 좋은 테스트를 보장하지 않는 이유는 단순히 코드를 실행했을 뿐 의미 있는 검증(assertion)이 없거나, 경계값 테스트나 예외 상황을 다루지 않을 수 있기 때문입니다. 실무에서는 핵심 비즈니스 로직은 80% 이상, 유틸리티나 단순 getter/setter는 낮은 목표를 설정하며, 커버리지보다 테스트의 품질과 의미 있는 시나리오 검증에 집중해야 합니다.

핵심 포인트
  • • Statement는 라인 실행, Branch는 조건 분기, Path는 모든 경로
  • • 높은 커버리지가 의미 있는 검증을 보장하지 않음
  • • 핵심 로직 중심으로 차등 목표 설정
  • • 커버리지보다 테스트 품질이 중요
답변에 넣으면 좋은 키워드
Statement Coverage Branch Coverage Path Coverage 테스트 품질 assertion 경계값 테스트
실무에서는

CI/CD 파이프라인에서 커버리지 임계값을 설정하여 코드 품질 게이트로 활용합니다.

Follow-up 질문

Mutation Testing은 무엇이며, 기존 코드 커버리지 측정 방식과 어떻게 다른가요?

4 통합 테스트 전략
Hard

Q. Spring Boot에서 @SpringBootTest, @WebMvcTest, @DataJpaTest의 차이점을 설명하고, 각각의 테스트 범위와 로딩되는 빈의 범위를 설명해주세요. 통합 테스트의 실행 속도를 개선하기 위한 방법도 함께 제시해주세요.

각 어노테이션이 로딩하는 ApplicationContext의 범위가 다르며, 이것이 테스트 속도에 영향을 줍니다.

A. 모범답안

@SpringBootTest는 전체 ApplicationContext를 로딩하여 실제 운영 환경과 유사한 통합 테스트를 수행하며, 모든 빈이 로딩되므로 가장 무겁습니다. @WebMvcTest는 MVC 레이어만 테스트하기 위해 Controller, ControllerAdvice, Filter 등 웹 관련 빈만 로딩하고, Service나 Repository는 모킹해야 합니다. @DataJpaTest는 JPA 관련 설정만 로딩하여 Repository 계층을 테스트하며, 기본적으로 인메모리 DB를 사용하고 트랜잭션이 자동 롤백됩니다. 통합 테스트 속도 개선을 위해서는 @MockBean 사용을 최소화하여 Context 재사용률을 높이고, 테스트 클래스별로 동일한 설정을 공유하며, Testcontainers 대신 인메모리 DB를 사용하거나, @DirtiesContext 사용을 피해야 합니다. 또한 슬라이스 테스트를 적극 활용하여 필요한 범위만 로딩하는 것이 효과적입니다.

핵심 포인트
  • • @SpringBootTest는 전체 Context 로딩
  • • @WebMvcTest는 웹 레이어만, @DataJpaTest는 JPA만
  • • MockBean 최소화로 Context 재사용
  • • 슬라이스 테스트로 필요한 범위만 로딩
답변에 넣으면 좋은 키워드
SpringBootTest WebMvcTest DataJpaTest 슬라이스 테스트 ApplicationContext MockBean
실무에서는

API 엔드포인트 테스트 시 전체 통합 테스트 대신 @WebMvcTest로 컨트롤러 레이어만 빠르게 검증합니다.

Follow-up 질문

@Transactional을 테스트 클래스에 선언했을 때와 프로덕션 코드에 선언했을 때 트랜잭션 경계가 어떻게 달라지며, 이것이 테스트 결과에 미치는 영향은 무엇인가요?

5 TDD와 리팩토링
Hard

Q. TDD(Test-Driven Development)의 Red-Green-Refactor 사이클을 설명하고, TDD를 실무에 도입할 때 겪을 수 있는 어려움과 이를 극복하기 위한 방법을 설명해주세요. 또한 레거시 코드에 테스트를 추가할 때의 전략도 함께 제시해주세요.

TDD는 테스트를 먼저 작성하여 설계를 개선하는 방법론이지만, 초기 학습 곡선과 레거시 코드 대응이 어려울 수 있습니다.

A. 모범답안

Red-Green-Refactor 사이클은 먼저 실패하는 테스트를 작성하고(Red), 테스트를 통과하는 최소한의 코드를 구현한 후(Green), 중복을 제거하고 코드를 개선하는(Refactor) 과정을 반복합니다. 실무 도입 시 초기 개발 속도 저하, 테스트 작성 미숙, 과도한 테스트로 인한 유지보수 부담 등의 어려움이 있으며, 이를 극복하려면 핵심 비즈니스 로직부터 시작하고, 페어 프로그래밍으로 학습하며, 테스트 가능한 설계 원칙을 익혀야 합니다. 레거시 코드에는 Characterization Test로 현재 동작을 보호하고, Seam을 찾아 의존성을 분리하며, Strangler Fig 패턴으로 점진적으로 리팩토링합니다. 또한 테스트하기 어려운 부분을 인터페이스로 추출하여 모킹 가능하게 만들고, 작은 단위부터 테스트를 추가해 나가는 것이 효과적입니다.

핵심 포인트
  • • Red-Green-Refactor 사이클 반복
  • • 핵심 로직부터 시작, 페어 프로그래밍 활용
  • • 레거시는 Characterization Test로 보호
  • • Seam 찾아 의존성 분리, 점진적 개선
답변에 넣으면 좋은 키워드
TDD Red-Green-Refactor Characterization Test Seam Strangler Fig 테스트 가능한 설계
실무에서는

새로운 기능 개발 시 TDD로 요구사항을 테스트로 먼저 정의하여 설계 품질을 높이고 회귀 버그를 예방합니다.

Follow-up 질문

Outside-In TDD와 Inside-Out TDD의 차이점을 설명하고, 각각 어떤 상황에서 적합한가요?

댓글 0

로그인 후 댓글을 작성할 수 있습니다.

아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!