[Spring] Test Code. TDD

강민승·2023년 9월 7일
0

spring

목록 보기
11/17

Test Code 작성하는 이유!

1번째 이유

Test 코드를 작성하지 않고 결과를 검증하는 과정은 비용이 많이 든다.

Test코드 사용 X (싱글 애플리케이션 (Monolithic Arichitecture)에서의 모습)

  1. 검증 코드 작성
  2. 애플리케이션 실행
  3. PostMan 혹은 브라우저 Request 요청
  4. log 혹은 print로 결과 검증
  5. 원하지 않는 결과 발생 시 애플리케이션 종료
  6. 다시 코드 작성

위와 같은 로테이션이 원하는 결과를 얻을 때까지 돌아가게 됩니다. 간단한 애플리케이션이라도 실행하고 종료하는데 비용이 많이 듭니다. 하지만 Test 코드를 작성한다면 이야기가 달라집니다.

Test 코드를 사용

  1. Test 코드 작성
  2. Test 코드 실행
  3. 결과 검증
  4. Test 코드 수정

애플리케이션을 실행, 종료할 필요가 없습니다. 따라서 비용이 줄어들고, Test 코드를 통해서 명확한 결과 검증이 가능합니다. Test 코드를 작성하지 않았을 때와 비교하면 이점이 명확합니다.

2번째 이유

Spring은 계층 구조로 일반적으로 아래와 같이 구성돼 있습니다.

Controller : 클라이언트 요청을 받고 클라이언트에게 결과를 반환 (Presentation Layer)

Service : 비즈니스 로직을 실행하고 결과 반환(Service Layer)

Repository : database에 쿼리를 이용해서 CRUD를 하는 계층(Data Access Layer)

Domain : Entity 클래스

그렇기에 애플리케이션을 실행해서 Test를 진행한다면, 어느 계층에서 잘못된 코드가 있는지 파악하는데 많은 비용이 듭니다. 하지만 Test 코드를 통해서 계층별로 Test를 진행한다면 어느 부분이 잘못된 지 파악을 쉽게 할 수 있습니다.

단위 테스트와 통합 테스트

단위(unit) 테스트

하나의 모듈을 기준으로 독립적으로 진행되는 가장 작은 단위 테스트 -> 쉽게 말하면 하나의 기능 혹은 메서드라고 이해하면 됩니다.

통합(integration) 테스트

모듈을 통합화는 과정에서 모듈 간의 호환성을 확인하는 테스트 -> unit이 하나였다면 반대로 여러 개의 계층이 테스트에 참여한 것이라고 생각하면 쉬울 거 같습니다.

단위 테스트 장단점

장점

  • 새로운 기능에 대해서 빠르게 작성 가능
  • Test 코드 자체가 하나의 문서
  • 시간과 비용의 절감

단점

  • 독립적인 테스트이므로 다른 객체와 상호작용 처리를 위해서 가짜 객체 정의 필요함
  • 가짜 객체의 답변 작성 필요함
  • 실제 운영 환경과 다른 답변을 내놓을 수 있는 가능성이 있음

통합 테스트 장단점

장점

  • 실제 객체를 사용하므로 가짜 객체 사용하지 않아 정의하지 않아도 됨
  • 실제 운영 환경과 같은 값을 도출 가능함

단점

  • 테스트 하나의 많은 비용이 들어감
  • 어느 계층에서 발생한 문제인지 파악하기 힘듦

단위 테스트, 통합 테스트를 선택한다면?

단위 테스트, 통합 테스트 모두 장단점이 명확합니다. 하지만 통합 테스트의 경우 비용을 절감할 수 있는 방법이 없습니다. 단위 테스트는 단점들을 개선해 나갈 수 있습니다. 그래서 좋은 단위 테스트 작성에 대해서 알아보겠습니다.

좋은 단위 테스트

  1. 1개의 테스트는 1개의 기능에 대해서만 테스트
  1. 테스트 주체와 협력자를 구분하기. ( 여기서 주체는 테스트를 할 객체이며, 협력자는 테스트를 진행하기 위해 정의하는 가짜 객체입니다.)
  1. Given, when, then으로 명확하게 작성하기

Given: 테스트를 진행할 행위를 위한 사전 준비
when: 테스트를 진행할 행위
then: 테스트를 진행한 행위에 대한 결과 검증

Test method

Junit lifecycle

Junit Main Annotation

@SpringBootTest

통합 테스트 용도로 사용됨
@SpringBootApplication을 찾아가 하위의 모든 Bean을 스캔하여 로드함
그 후 Test용 Application Context를 만들어 Bean을 추가하고, MockBean을 찾아 교체

@ExtendWith

JUnit4에서 @RunWith로 사용되던 어노테이션이 ExtendWith로 변경됨
@ExtendWith는 메인으로 실행될 Class를 지정할 수 있음
@SpringBootTest는 기본적으로 @ExtendWith가 추가되어 있음

@WebMvcTest(Class명.class)

( )안에 작성된 클래스만 실제로 로드하여 테스트를 진행
매게변수를 지정해주지 않으면 @Controller, @RestController, @RestControllerAdvice 등 컨트롤러와 연관된 Bean이 모두 로드됨
스프링의 모든 Bean을 로드하는 @SpringBootTest대신 컨트롤러 관련 코드만 테스트할 경우 사용

@Autowired about Mockbean

Controller의 API를 테스트하는 용도인 MockMvc 객체를 주입 받음
Perform()메소드를 활용하여 컨트롤러의 동작을 확인할 수있음
andExpect(), andDo(), andReturn() 등의 메소드를 같이 활용함

@MockBean

테스트할 클래스에서 주입 받고 있는 객체에 대해 가짜 객체를 생성해주는 어노테이션
해당 객체는 실제 행위를 하지 않음
given() 메소드를 활용하여 가짜 객체의 동작에 대해 정의하여 사용할 수 있음

@AutoConfigureMockMvc

spring.test.mockmvc의 설정을 로드하면서 MockMvc의 의존성을 자동으로 주입
MockMvc 클래스는 REST API 테스트를 할 수 있는 클래스

@Import

필요한 Class들을 Configuration으로 만들어 사용할 수 있음
Configuration Component 클래스도 의존성 설정할 수 있음
Import된 클래스는 주입으로 사용 가능

assert메서드

  • assertEquals(a, b): a와 b의 값이 동일한지 확인

  • assertEquals(member.getName(), findMember.getName());

  • assertSame(a, b): a와 b의 객체가 동일한지 확인

  • assertNull(a): a가 null인지 확인

  • assertNotNull(a): a가 null이 아닌지 확인

  • assertTrue(a): a가 true인지 확인

  • assertFalse(a): a가 false인지 확인

  • assertThrows(입셉션 에러 종류 a, 발생하는 로직 b) : b 로직시에 a 입셉션이 발생하는지 확인

    코드

    IllegalStateException e = assertThrows(IllegalStateException.class,
            () -> memberService.join(member2));//예외가 발생해야 한다.
  • assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

  • assertThat : AssertJ 라이브러리에 포함된 메서드, 어떤 조건이 참인지 확인
    import static org.assertj.core.api.Assertions.*;

    코드

    IllegalStateException e = assertThrows(IllegalStateException.class,
            () -> memberService.join(member2));//예외가 발생해야 한다.
    
    assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

출처
출처

profile
Step by Step goes a long way. 꾸준하게 성장하는 개발자 강민승입니다.

0개의 댓글