Skip to content

Latest commit

 

History

History
284 lines (191 loc) · 11.5 KB

README.md

File metadata and controls

284 lines (191 loc) · 11.5 KB

테스트 코드란?

  • 소프트웨어의 기능과 동작을 테스트하는데 사용되는 코드

단위 테스트 (Unit test)

  • 작은 코드 단위를 독립적으로 검증하는 테스트 (클래스 & 메서드)
  • 검증 속도가 빠르고 안정적으로 테스트 가능

통합 테스트 (Integration test)

  • 단위테스트만으로는 기능 전체의 신뢰성을 보장할 수 없다.(클래스 & 메서드 단위의 작은 범위이기 때문에)
  • 단위 테스트에서 검증된 개별 기능들을 결합하여 예상대로 상호작용 하고 있는지를 확인하는 테스트



Test 방법론


TDD (Test Driven Development)

  • 테스트가 전체 개발을 주도해 나가는 방법론으로, 비즈니스 코드같은 프로덕션 코드를 먼저 작성하는 것이 아닌 테스트가 구현 과정을 주도하도록 하는 방법론이다.
  • 테스트가 어려운 영역을 자연스럽게 분리하여 테스트 가능한 코드를 더 수월하게 짤 수 있게 도와준다.
  • 해피케이스 뿐만 아니라 쉽게 생각하기 어려운 엣지 케이스를 생각하는데 도움을 준다.
  • TDD 단계
    1. RED : 실패하는 테스트 작성
    2. GREEN : 테스트 통과를 위한 최소한의 코드 작성
    3. REFACTOR : 리팩토링을 하면서 구현 코드 개선 (테스트 통과는 계속 유지)

BDD (Behavior Driven Development)

  • TDD에서 파생된 테스트 개발 방법
  • 함수 단위 테스트에 집중하기보다, 시스템 동작의 행위를 기반으로 한다. (시나리오에 기반한 테스트 케이스 자체에 집중하여 테스트)
    • GIVEN : 시나리오 진행에 필요한 준비 과정 (필요 값 명시)
    • WHEN : 시나리오 행동 진행 (필요 조건 명시)
    • THEN : 시나리오의 결과 명시, 검증 (결과 명시)



수동 테스트 vs 자동 테스트

  • 수동 테스트 : QA(Quality Assurance)같은 사람이 수동으로 테스트를 진행
  • 자동 테스트 : 스크립트 & 코드 등 자동화 도구를 사용하여 테스트를 진행

테스트 케이스

  • 성공 테스트
  • 예외 테스트
  • 성공 테스트만큼 예외 테스트를 하는 것도 중요하다.



테스트 : 꼼꼼한 단위 테스트 + 큰 기능 단위를 검증하는 통합 테스트 
- 테스트코드가 병목된다면 테스트 코드 자체가 짐이 될 수 있고 잘못된 검증이 이루어지기 쉽다. (안정성 제공 X)

- 올바른 테스트 코드
  - 수동 테스트를 최소화 할 수 있으며, 빠른 시간 안에 버그를 발견할 수 있으며 소프트웨어의 빠른 변화에 효과적으로 대응할 수 있는 자동화 된 코드




JUnit5

  • 단위 테스트를 위한 테스트 프레임워크
  • 자바 기반으로 동작한다. (자바 8버전 이상부터 사용 가능)
  • junit5 공식 홈페이지
  • 여러 어노테이션 제공
    • @Test : 테스트 메서드임을 선언하는데 사용
    • @DisplayName : 테스트 클래스 & 메서드의 사용자 정의(설명)을 선언하는데 사용
    • @BeforeEach : 각각의 테스트 메소드가 실행되기 전에 실행되어야 하는 메서드를 지정하는데 사용
    • @AfterEach : 각각의 테스트 메소드가 실행된 후에 특정 정리 작업을 하는 메서드를 지정하는데 사용
    • @BeforeAll : static으로 선언되며, 모든 테스트 메소드가 실행되기 전에 딱 한 번 호출되어야 하는 정적 메소드를 지정하는데 사용
    • @AfterAll : static으로 선언되며, 모든 테스트 메소드가 실행된 후에 딱 한 번 호출되어야 하는 정적 메소드를 지정하는데 사용
    • @Nested : 중첩된 테스트 클래스를 정의하는 데 사용되어, 테스트를 논리적으로 그룹화하고 클래스 구조를 명확하게 표현하는데 사용

AssertJ

  • 테스트 코드 작성을 원활하게 돕는 테스트 라이브러리
  • 객체의 상태나 동작을 검증하는데 사용
  • AssertThat(검증 대상)으로 시작하여 메서드 체이닝을 통해 가독성 있는 코드 작성 가능
  • testImplementation 'org.springframework.boot:spring-boot-starter-test' 해당 의존성을 추가하면 AssertJ 추가 가능



통합 테스트 환경 구성 관련 어노테이션


  • @SpringbootTest
    • 전체 Spring 컨텍스트를 로드하여 통합 테스트를 수행하는 데 사용
    • 이 애노테이션은 애플리케이션의 모든 빈을 로드하고, 애플리케이션이 실제로 실행되는 것처럼 환경 구성

  • @WebMvcTest
    • Spring MVC 컴포넌트(예: 컨트롤러)를 테스트하는 데 사용
    • 웹 레이어에 집중하여 컨트롤러와 관련된 빈들만 로드하고, 다른 빈들은 로드하지 않음
    • @WebMvcTest(controller = <컨트롤러명>.class) 로 원하는 controller 클래스를 선언하여 사용

  • @DataJpaTest
    • JPA 리포지토리 계층을 테스트하는 데 사용
    • 이 애노테이션은 데이터베이스 관련 컴포넌트만 로드하고, 데이터베이스를 위한 자동 설정 제공
    • 주로 인메모리 데이터베이스(H2 등)를 사용하여 테스트 수행



Mock

  • 모의, 가짜의 라는 뜻으로 실제 객체를 만들기엔 비용과 시간이 많이 들기 때문에 실제 객체와 동일한 모의 객체를 만드는 것을 말함
  • 테스트할 때 필요한 실제 객체와 동일한 모의 객체를 만들어 테스트의 효용성을 높이기 위해 사용한다.

Mock 분류 : Test Double

  • 영화의 스턴트 더블(stunt double)에서 차용

  • 테스트를 진행하기 어려운 경우 이를 대신해 테스트를 진행할 수 있도록 만들어주는 객체('가짜' 컴포넌트를 의미)

  • Dummy

    • 아무것도 하지 않는 깡통 객체 (실제 객체를 모방하기만 함)
    • 파라미터 리스트를 채우기 위해 필요한 객체(실제 작동 X)
  • Fake

    • 단순한 형태로 실제 객체의 행위와 동일한 기능은 수행하나, 심플한 기능으로 수행
    • 프로덕션에서 쓰기에는 부족한 객체
  • Stub

    • 테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체
    • 어떤 클래스의 어떤 메서드를 실행했을때의 return 값을 정의함
    • 정의하지 않은 그 외의 요청에는 응답 X
  • Spy

    • Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체
    • 일부는 실제 객체처럼 동작시키고 일부만 Stubbing 할 수 있다.
    • 기본적으로는 실제 객체처럼 동작하지만, Stubbing 할 것을 지정하면 특정 기능만 내가 원하는 방식으로 동작하게 할 수 있다.
    • 자주 사용하지는 않음 (기본적으로 Mock을 씀)
  • Mock

    • 행위에 대한 기대를 정의하고 그에 따라 동작하도록 만들어진 객체


Stub과 Mock을 구분할 필요가 있다.
Stub : 상태 검증
Mock : 행위 검증



Mockito

  • 단위 테스트를 위해 모의 객체를 생성하고 관리하는데 사용하는 java 오픈소스 프레임워크
  • Mockito 공식 홈페이지

Mocking

  • 스프링이 뜰 경우 (스프링 컨텍스트가 있는 경우) - 통합 테스트 상태 : @MockBean @SpyBean 사용
  • 스프링이 안 뜰 경우 (스프링 컨텍스트가 없는 경우) - 단위 테스트 상태 : Mockito.mock(클래스 명.class), @Mock, @Spy @InjectMocks
  • Mock 객체에 기본적으로 아무것도 지정하지 않으면 예외가 발생하는 것이 아닌, 기본값으로 자동 설정됨

Mock관련 어노테이션


  • @MockMvc
    • 컨트롤러 레이어를 단위 테스트할 때 유용하며, 실제 서버를 구동하지 않고도 HTTP 요청을 생성하고 응답을 검증할 수 있게 해줌

  • @Mock
    • Mock 객체를 생성할 때 사용
    • 생성된 Mock 객체는 실제 객체 대신 사용되며, 특정 동작을 정의하고 호출 검증이 가능하다.
    • @ExtendWith(MockitoExtension.class) 어노테이션을 통해 Mock 어노테이션을 활성화 할 수 있다.

  • @MockBean
    • 통합 테스트 환경에서, 스프링 컨텍스트 내에서 특정 빈을 모킹할때 사용
    • 스프링 컨텍스트에 있는 Bean 객체를 Mockito의 Mock 객체로 갈아 끼우겠다는 의미 (실제 빈 대신 모킹 빈 주입하여 테스트 환경 구성)

  • @Spy
    • 실제 객체를 감시하는 스파이 객체를 생성하여 일부 메서드를 모킹하거나 실제 동작을 사용

  • @SpyBean
    • 통합 테스트 환경에서, 스프링 컨텍스트 내에서 특정 빈을 모킹할때 사용
    • 스프링 컨텍스트에 있는 Bean 객체를 Mockito의 Spy 객체로 갈아 끼우겠다는 의미 (실제 빈 대신 모킹 빈 주입하여 테스트 환경 구성)

  • @InjectMocks
    • 테스트 대상 클래스의 인스턴스를 생성하고, 그 클래스가 의존하는 객체들을 Mock 객체들(@Mock, @Spy)로 자동으로 주입해주는 역할
    • 테스트 대상 클래스의 생성자, 필드, 또는 세터 메서드를 통해 의존성을 주입할 수 있습니다.

  • @ExtendWith(MockitoExtension.class)
    • JUnit 5와 함께 Mockito를 사용하기 위한 어노테이션이다.
    • Mockito의 어노테이션(@Mock, @InjectMocks 등)을 쉽게 사용할 수 있도록 설정해준다.
      • 실제로, 위와 같은 어노테이션을 사용할때 설정이 없다면 에러를 발생시킨다.
      • Mock객체를 직접 구현하면(Mockito.mock(<class명>.class)), 사용 X



테스트 코드 TIPS

  • 한 테스트 코드는 하나의 기능만을 테스트 해야한다.
    • 분기문, 반복문 등은 최대한 지양하는 것이 좋다 -> 이것이 있다는 것 자체가 여러 기능에 관한 테스트라고 할 수 있기 때문에
    • 분기문(If문) 일 경우, 이에 대한 상황을 분리하여 테스트 코드를 작성하는 편이 테스트코드를 더 명시적으로 보여줄 수 있다.

  • 테스트 코드는 개발자가 다 제어할 수 있는 요소를 사용해야한다.
    • LocalDate.now() 같이 테스트 시점이나 상황(timezone)으로 달라질 가능성이 존재할 경우 지양하는 것이 좋다.

  • 테스트 간에 독립성을 보장해야한다.
    • 테스트 간의 순서가 존재하면 X
    • 테스트 간의 공유 변수 존재 X

  • 테스트 코드 내의 data.sql은 데이터 파편화가 생겨 유지보수 포인트가 될 수 있기 때문에 권장 X

  • 테스트 코드의 최적화를 통해 springboot가 띄워지는걸 줄이는 것이 테스트 비용을 낮출 수 있다.

  • private 메서드는 테스트 할 필요가 없다. -> 해야 할 것 같다면 해당 메서드를 다른 객체로 분리할 필요가 있는지를 확인해야 한다.
    • private은 외부에 공개하기 싫은 메서드이기 때문이다. 또한, 어짜피 public 메서드를 테스트할 때 자동으로 테스트가 된다.

  • DisplaName 어노테이션의 경우 확실한 네이밍이 필요하다
    • 명사의 나열보다는 문장으로 끝내는 것이 더 직관적이여서 좋다.
    • 테스트 행위 + 테스트의 결과까지 문장에 추가하는 것이 좋다.
    • 도메인 정책 관점 용어를 사용하여 문장을 구성하는 것이 좋다.