Python 시니어 테스트·코드품질 기술면접

Python 시니어 (7년+) 테스트 · 코드품질 3문항 조회수 89 · 2026-08-04 (화) 22:10:53
1 테스트 더블 전략
Hard

Q. Python에서 Mock, Stub, Spy, Fake의 차이점을 설명하고, unittest.mock의 Mock과 MagicMock의 차이를 설명해주세요. 외부 API 의존성이 있는 서비스 레이어를 테스트할 때 각각의 테스트 더블을 어떤 기준으로 선택하시나요? 또한 과도한 Mocking이 테스트의 가치를 떨어뜨리는 상황과 이를 개선하기 위한 전략을 설명해주세요.

각 테스트 더블의 검증 방식과 목적의 차이, 그리고 테스트가 구현이 아닌 행위를 검증해야 하는 이유를 생각해보세요.

A. 모범답안

Mock은 호출 검증에 집중하고, Stub은 미리 정의된 응답을 반환하며, Spy는 실제 객체를 래핑해 호출을 기록하고, Fake는 실제 동작하는 경량 구현체입니다. MagicMock은 매직 메서드를 자동으로 지원하는 Mock의 확장입니다. 외부 API 테스트 시 단순 응답만 필요하면 Stub을, 호출 순서나 인자 검증이 필요하면 Mock을, 복잡한 로직이 있으면 Fake를 사용합니다. 과도한 Mocking은 테스트가 구현 세부사항에 결합되어 리팩토링 시 깨지기 쉽고, 실제 통합 문제를 놓칠 수 있습니다. 개선을 위해 통합 테스트 비중을 높이고, 테스트 가능한 설계(의존성 주입, 인터페이스 분리)를 적용하며, 중요한 경로는 실제 구현체로 테스트하는 것이 좋습니다.

핵심 포인트
  • • Mock/Stub/Spy/Fake의 명확한 구분과 사용 시나리오
  • • unittest.mock의 Mock과 MagicMock 차이
  • • 과도한 Mocking의 문제점(구현 결합, 통합 이슈 누락)
  • • 테스트 가능한 설계와 통합 테스트 균형
답변에 넣으면 좋은 키워드
Mock Stub Spy Fake MagicMock 테스트 더블 의존성 주입 통합 테스트
실무에서는

마이크로서비스 아키텍처에서 외부 서비스 의존성을 격리하여 빠르고 안정적인 단위 테스트를 작성할 때 필수적입니다.

Follow-up 질문

pytest-mock과 unittest.mock의 차이점은 무엇이며, 어떤 상황에서 각각을 선호하시나요? 또한 Monkey Patching을 사용한 테스트의 위험성과 대안을 설명해주세요.

2 테스트 커버리지 전략
Hard

Q. 테스트 커버리지(Line, Branch, Path Coverage)의 차이를 설명하고, 높은 커버리지가 반드시 좋은 테스트를 의미하지 않는 이유를 설명해주세요. Python에서 coverage.py를 사용할 때 커버리지 측정의 한계점과, Mutation Testing(mutmut, cosmic-ray)이 어떻게 테스트의 실효성을 검증하는지 설명해주세요. 시니어 개발자로서 팀의 커버리지 목표를 설정할 때 어떤 기준과 전략을 사용하시나요?

커버리지는 코드 실행 여부만 측정할 뿐 테스트의 품질이나 assertion의 적절성은 보장하지 못한다는 점을 고려하세요.

A. 모범답안

Line Coverage는 실행된 라인, Branch Coverage는 분기 조건의 true/false 경로, Path Coverage는 가능한 모든 실행 경로를 측정합니다. 높은 커버리지라도 의미 없는 assertion이나 edge case 누락이 있으면 버그를 놓칠 수 있습니다. coverage.py는 실행 여부만 측정하므로 assertion의 적절성이나 테스트 로직의 정확성은 검증하지 못합니다. Mutation Testing은 코드를 의도적으로 변형하여 테스트가 이를 감지하는지 확인함으로써 테스트의 실효성을 검증합니다. 커버리지 목표 설정 시 핵심 비즈니스 로직은 90% 이상, 유틸리티는 80% 이상으로 차등 적용하고, 단순히 수치 달성보다 중요 시나리오와 edge case 검증에 집중하도록 가이드합니다.

핵심 포인트
  • • Line/Branch/Path Coverage의 차이와 각각의 의미
  • • 높은 커버리지의 한계(assertion 품질, edge case 누락)
  • • Mutation Testing의 원리와 테스트 실효성 검증
  • • 도메인별 차등 커버리지 목표와 품질 중심 접근
답변에 넣으면 좋은 키워드
Line Coverage Branch Coverage Path Coverage Mutation Testing coverage.py mutmut assertion edge case
실무에서는

CI/CD 파이프라인에서 커버리지 게이트를 설정하고 코드 리뷰 시 테스트 품질을 평가하는 기준으로 활용됩니다.

Follow-up 질문

레거시 코드베이스에 테스트를 추가할 때 커버리지를 점진적으로 높이는 전략과, 테스트하기 어려운 코드(하드코딩된 의존성, 전역 상태)를 리팩토링하는 접근법을 설명해주세요.

3 테스트 격리와 상태 관리
Hard

Q. 대규모 테스트 suite에서 테스트 간 상태 공유로 인한 Flaky Test 문제를 경험한 적이 있다면 공유해주세요. Python에서 테스트 격리를 보장하기 위한 전략(database transaction rollback, fixture isolation, test container)을 설명하고, pytest의 fixture scope 설정과 autouse의 장단점을 실무 관점에서 설명해주세요. 또한 통합 테스트에서 데이터베이스 상태를 관리하는 방법(factory pattern, fixture file, database seeding)의 트레이드오프를 비교해주세요.

테스트 실행 순서에 따라 결과가 달라지는 원인과 각 테스트가 독립적으로 실행될 수 있도록 보장하는 방법을 생각해보세요.

A. 모범답안

Flaky Test는 주로 공유 데이터베이스 상태, 전역 변수, 시간 의존성, 비동기 타이밍 이슈에서 발생합니다. 테스트 격리를 위해 각 테스트마다 database transaction을 rollback하거나, Docker Testcontainers로 독립 환경을 구성하거나, pytest fixture의 function scope를 사용합니다. autouse fixture는 명시적 의존성 없이 자동 실행되지만 암묵적 동작으로 디버깅이 어려워질 수 있습니다. 데이터 관리는 Factory Boy 같은 factory pattern이 유연하고 재사용 가능하지만 복잡하고, fixture file은 간단하지만 유지보수가 어려우며, seeding script는 일관성 있지만 느립니다. 실무에서는 factory pattern을 기본으로 하고 복잡한 시나리오는 fixture file로 보완하는 하이브리드 전략을 선호합니다.

핵심 포인트
  • • Flaky Test의 주요 원인(상태 공유, 시간 의존성, 비동기)
  • • 테스트 격리 전략(transaction rollback, Testcontainers, fixture scope)
  • • autouse fixture의 장단점과 명시적 의존성의 중요성
  • • 데이터 관리 방법별 트레이드오프와 하이브리드 전략
답변에 넣으면 좋은 키워드
Flaky Test 테스트 격리 transaction rollback Testcontainers Factory Boy fixture scope autouse database seeding
실무에서는

CI 환경에서 병렬 테스트 실행 시 테스트 간 간섭을 방지하고 재현 가능한 테스트 결과를 보장하는 데 필수적입니다.

Follow-up 질문

비동기 코드(asyncio)를 테스트할 때 pytest-asyncio를 사용하는 방법과, 시간 의존적인 코드를 테스트하기 위해 freezegun이나 time-machine 같은 라이브러리를 활용하는 전략을 설명해주세요.

댓글 0

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

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