본문 바로가기
CodeStatesBootCamp/Review

Section 3 - Unit 7 : [Spring MVC] 테스팅 Review

by JKROH 2023. 4. 28.
반응형
Review 에서는 학습한 내용을 다시금 기록합니다.
Unit Review는 학습한 내용 중 기존에 알고 있었지만 정확하게 이해하지 못하던 정보와 새롭게 알게된 정보를 기록합니다. 추가적인 설명을 요하는 부분은 댓글로 남겨주세요.
Section Review는 전반적인 Section을 되돌아보고 학습했던 시간과 과정, 내용을 총괄하여 기록합니다.

단위 테스트

* 단위 테스트란?

- 기능 테스트

  • 전체 애플리케이션 단위에서, 애플리케이션이 제공하는 기능이 올바르게 동작하는지를 확인하는 테스트를 의미한다.
  • 기능 테스트를 진행하기 위해서는 외부 클라이언트와 함께 연관해서 테스트를 진행해야 한다. 따라서 단위 테스트로 부르기는 어렵다.

- 통합 테스트

  • 외부 클라이언트와의 연동 없이 내부적인 테스트 코드만으로 애플리케이션의 동작을 확인하는 테스트를 의미한다.
  • 통합 테스트 여러 계층이 연관되어 진행되기 때문에 단위 테스트로 부르기는 어렵다.

- 슬라이스 테스트

  • 애플리케이션의 특정 계층별로 해당 계층의 동작을 확인하는 테스트를 의미한다.
  • 여전히 외부의 데이터를 요구하거나, 내부적으로 DB와 연동하여 테스트하는 등의 과정이 필요하기 때문에 단위 테스트로 부르기는 어렵다.

- 단위 테스트

  • 서비스 계층의 핵심 비즈니스 로직을 대상으로 해당 로직의 동작을 확인하는 테스트를 의미한다.
  • 하나의 메서드 단위로 테스트가 진행되기 때문에 별도의 외부 API와 연동의 과정이 필요없다. 

 

* 단위 테스트를 위한 F.I.R.S.T 원칙

- First

  • 단위 테스트는 빨라야 한다. 테스트가 느리다면 테스트 케이스를 작성하는 의미가 없다.

- Independent

  • 단위 테스트는 독립적이어야 한다. 다른 테스트에 종속적이면 안된다.
  • A라는 테스트의 결과는 다른 테스트들의 실행과는 관계가 없어야 한다는 말이다.

- Repeatable

  • 단위 테스트는 반복 수행이 가능해야 한다.
  • 몇 번을 돌려도 같은 결과를 보여야 한다는 말이다.

- Self-validating

  • 단위 테스트는 스스로 결과의 옳고 그름을 판단할 수 있어야 한다.

- Timely

  • 단위 테스트는 테스트하고자 하는 기능을 구현하기 직전에 작성되어야 한다.
  • TDD에 적합한 원칙이지만 지키기 쉽지 않다.

슬라이스 테스트

* API 계층 테스트

  • Spring MVC에서는 컨트롤러가 API 계층에 해당한다.
  • 즉, API 계층을 테스트한다는 것은 컨트롤러를 테스트하는 것이다.

- 컨트롤러 테스트를 위한 테스트 클래스 구조

  • 테스트 클래스에 @SpringBootTest 애너테이션을 붙인다.
    • 해당 애너테이션은 Spring Boot 기반의 애플리케이션을 테스트하기 위한 Application Context를 생성한다.
  • 테스트 클래스에 @AutoConfigureMockMvc 애너테이션을 붙인다.
    • 해당 애너테이션을 통해 테스트에 필요한 애플리케이션의 구성이 자동으로 진행된다.
    • 후술할 MockMvc 등의 기능을 이용하기 위해서도 해당 애너테이션이 필요하다.
  • MockMvc를 주입받는다.
    • MockMvc는 서버를 실행하지 않고도 애플리케이션 내부에서 컨트롤러의 테스트 환경을 지원해주는 일종의 프레임워크다.

 

* 데이터 액세스 계층 테스트

- 데이터 액세스 계층 테스트를 위한 규칙

  • 모든 테스트가 시작되는 시점에서의 DB 상태는 동일해야 한다.
  • 테스트는 순서가 보장되지 않는다. 따라서 만일 다른 테스트의 결과로 인한 DB의 변동사항이 초기화되지 않으면, 하나의 테스트의 결과가 다른 테스트에 영향을 미칠 수 있다.

- 데이터 액세스 계층 테스트를 위한 테스트 클래스 구조

  • 테스트 클래스에 @DataJpaTest 또는 @DataJdbcTest 애너테이션을 붙인다.
    • 해당 애너테이션을 통해 테스트하고자 하는 레포지토리의 기능을 사용하기 위한 Configuration이 진행된다.
    • 또한 해당 애너테이션은 @Transactional 애너테이션을 포함하고 있기 때문에, 하나의 테스트 케이스의 실행이 종료되는 시점에 DB에 저장된 데이터가 롤백된다.
      • JUnit을 통한 Test가 실행된다면 실행된다면 @Transactional이 적용되어있는 로직은 SELECT를 제외한 모든 쿼리를 Rollback 대상으로 취급한다.

Mockito

* Mockito

- Mockito란?

  • 테스트를 위한 Mock 객체를 사용하게 해주는 Mocking Library다.

- 테스트에서 Mock 객체를 사용하는 이유

  • 컨트롤러를 테스트한다고 해보자.
    • 예를 들어, postMember()를 테스트한다고 하면, 해당 기능의 실질적인 비즈니스 로직을 수행하기 위한 서비스 계층의 메서드를 호출하여 실행한다.
    • 해당 메서드는 데이터 액세스 계층의 메서드를 호출하여 실행한다.
    • 결국, 컨트롤러를 슬라이스 테스트하려고 했지만, 모든 계층이 연관되어 실행되기 때문에 슬라이스 테스트로 보기 어렵다. 오히려 통합 테스트에 가깝다.
  • 그러나 Mock 객체를 사용하면 MemberService 대신 MockMemberService에서 기능을 수행하고 끝낸다. Mock 객체를 사용하지 않을 때보다 연관된 계층의 수가 적어진다. 즉, 테스트가 단순화되어 테스트하고자 하는 대상에만 집중할 수 있다.

- Spring을 사용하여 Mockito 적용

  • Mock 객체를 만들어 테스트 클래스에 주입할 클래스 필드에 @MockBean 애너테이션을 붙인다.
    • 위의 예시라면, MemberService 필드에 해당 애너테이션을 붙이면 된다.
  • Stubbing 메서드인 given() 또는 when()을 사용하여 stub데이터를 생성한다.
    • Stubbing은 테스트를 위해 Mock 객체가 항상 동일한 동작을 하도록 지정하는 것을 의미한다. 
    • 위의 두 메서드는 기능적으로 동일하다.
    • 다만 BDD의 관점에서 보았을 때, given - will return 구조가 when - then return의 구조보다 좀 더 읽기 편하다. 테스트 코드도 코드다. 읽기 좋은 테스트를 작성하는 것은 중요하다.
  • given()에는 사용하고자 하는 Mock 객체의 메서드를 넣어준다.
    • 해당 메서드에 인자가 필요하다면, Mockito.any()에 필요한 인자의 클래스 타입을 넣어 사용한다.
  • 마지막으로 willReturn()을 통해 given()에 들어간 메서드가 반환할 Stub 데이터를 지정한다.
given(memberService.createMember(Mockito.any(Member.class)))
        .willReturn(member);

- JUnit을 사용하여 Mockito 적용

  • 클래스에 @ExtendWith(MockitoExtension.class) 애너테이션을 붙인다.
  • Mock 객체로 생성할 필드에 @Mock 애너테이션을 붙인다.
  • @Mock 애너테이션이 붙은 Mock 객체를 주입받을 Mock 객체에 @InjectMocks를 붙인다.
    • 예를 들어 서비스 계층을 테스트할 때, Service 객체와 Repository객체가 필요하다. 이 때 Service 객체는 Repository객체를 주입받아야한다.
    • 이 때, 필드 멤버인 Repository에는 @Mock을 붙여 Mock 객체를 만든다.
    • 또 다른 필드 멤버인 Service에는 @InjectMocks를 붙여 Repository를 주입받은 Mock 객체를 만든다.
반응형

댓글