1. 시작하게 된 계기 및 다짐 😮
  • 이번 코드스테이츠의 백엔드 엔지니어링 개발자 부트캠프에 참여하게 되면서 현직개발자 분들의 빠른 성장을 위한 조언 중 자신만의 블로그를 이용하여 배운 것 들을 정리하는게 많은 도움이 된다 하여 시작하게 되었다.

    • 그 날 배웠던 것을 길지 않아도 좋으니 정리하며 복습하는 습관 기르기
    • 주말에 다음주에 배울 내용들을 예습
    • 코딩 문제와 java코드들은 꾸준히 학습
    • 자료구조를 이용한 알고리즘 문제 해결 학습
  1. 학습 목표 😮
목표결과
단위테스트 이해 및 적용 방법학습O
Junit 사용 및 미사용 적용 방법O
Hamcrest이해 및 사용O
Slicing Test 이해 및 적용 방법 학습O
Mockito 및 TDD 이해O
  1. 정리 😮

단위 테스트


1. 단위 테스트(Unit Test)

  1. 단위테스트란

    • 기능[전체]/통합[Front쪽 제외]/슬라이스[API계층별] 이후 , 클래스의 메서드 단위로 테스트하는 단위 테스트
    • 주로, 비지니스 로직에서 사용하는 클래스들이 독립적으로 단위테스트라 부른다
    • 테스트케이스를 기반으로 빠르게 구현한 코드가 의도대로 동작하는지 확인가능
    • given - when - then 이라는 용어는 BDD(Behavior Driven Development) 테스트 방식에서 사용하는 용어
  2. 단위테스트를 위한 F.I.R.S.T 원칙

    1). Fast

    • 작성한 테스트 케치스는 빨라야 한다.

    2). Independent

    • 테스트 클래스 안의 메서드들이 서로 독립적이여아 한다.
    • 순서와 상관없이 실행이 보장되어야함

    3). Repeatable

    • 어떤 환경에서도 반복해서 실행이 가능하여야 한다.
    • 외부 서비스/리소스와 연동은 가급적 끊어주어야 한다.

    4). Self-vaildating

    • 단위 테스트 성공/실패 라는 자체 검증 결과를 보여주어야 한다.

    5). Timely

    • 테스트 하려는 기능 구현을 하기 직전에 작성해야 한다.

2. JUnit없이 비지니스 로직에 단위 테스트 적용

  • 직접 테스트 데이터를 임의로 만들고 이 값을 넣어 비교
  1. Given-When-Then 표현 스타일

    1). Given

    • 테스트에 필요한 전제조건들로, 전달되는 입력값등이 포함됨

    2). when

    • 테스트 할 동작(대상)을 지정하고 메서드 호출을 통해 보통 한두줄로 해결

    3). Then

    • 예상 결과값과 실제 결과값이 일치하는지 검증(Assertion)을 하는 코드들

Extra

  1. 유틸리티 클래스

    • 클래스 객체로 인스턴스화 될 필요가 없는 클래스로, 해당 클래스 내부 메서드들을 static으로 구성
  2. Assertion

    • 테스트 결과를 검증시에 사용하는 용어로, 예상하는 결과 값이 참이길 바라는 것 이다.



JUnit을 사용한 단위테스트


0. @애너테이션

  1. @Test

    • 테스트하고 하는 대상(주로 메서드)에 붙임
    • import org.junit.jupiter.api.Test;
  2. @DisplayName(" Tst " )

    • 실행 결과창에 실제로 표시되는 이름을 바꾸는 것으로, 여기서는 Tst로 나옴
  3. @BeforeEach

    • 테스트케이스 실행 전 각 메서드마다 실행되는 메서드를 나타낼 떄 사용한다.
  4. @BeforeAll

    • 클래스 레벨에서 테스트 케이스 전체를 실행하기전에 딱 한번 실행되는 메서드를 나타낼때 사용
    • 해당 메서드는 static 메서드여야 한다.
  5. @AfterEach, AfterAll

    • 호출되는 시점만 반대일 뿐 나머지 행동은 모두 같음

1. JUnit 이해

  1. JUnit

    • Spring boot에서 어플리케이션을 테스트하기 위해 사용하는 Default 테스트 프레임워크
    • 표준적으로 Spring boot에서는 이를 사용
    • 실행 순서는 보장되지 않는다.
  2. JUnit 작성법

    • testImplementation 'org.springframework.boot:spring-boot-starter-test' 안에 포함되어 있음
    • 일반적으로, Spring boot Initializr를 사용하면 기본적으로 포함된다.
  3. Assertion 메서드

    1). assertEquals(expected, actual)

    • 기대값과 실제 값이 같은지 검증이 가능

    2). assertNotNull(변수,"should be not null");

    • 변수가 널인지 아닌지 확인하여 널일 경우 두번째 파라미터의 메시지를 표시한다.

    3). assertThrows(NullPointerException.class, () -> getCryptoCurrentcy("XRP"))

    • 첫번째 파라미터에서 발생이 기대되는 예외 클래스를 입력하고, 두번째에 람다 표현식에서의 테스트 대상 메서드 호출
    • 예외 타입이 다를경우 테스트 실행 결과는 failed가 된다.
    • 단, 상위 타입의 예외(NullPoint -> RuntimeException)을 첫번째 파라미터로 넣어주면 passed가 된다.

    4). assertDoesNotThrow(()->getCryptoCurrency("XRP"))

    • 예외가 발생하지 않을것이라 예상

    5). assertThat(totalActual,is(total));

    • assertEquals와 같은 의미
  4. Assumption을 이용한 테스트

    • 특정 환경에서만 테스트 케이스가 실행되도록 설정가능

    1). assumeTrue(System.getProperty("os.name").startsWith("Windows"));

    • 메서드의 파라미터가 true이면 나머지 로직들이 실행된다.

Extra

  1. Assertion
  1. Assumption



Hamcrest를 사용한 Assertion


1. Hamcrest

  1. Hamcrest란

    • JUnit Assertion보다 아래 이유로 더 많이 사용됨
    • JUnit 기반의 단위 테스트에서 사용하는 Assertion Framework
    • Assertion을 위한 매쳐(Matcher)가 더 자연스러운 문장으로 가독성이 높고 다양한 Matcher 제공
    • 테스트 실패 메시지를 이해하기 쉽다.
  2. Hamcrest 메소드

    1). assertThat(actual, is(equalTo(expected)));

    • 첫번째 파라미터 : 테스트 대상의 실제 결과값
    • 두번째 파라미터 : 기대하는 기댓값
    • 첫번째 파라미터가 두번째 matcher함수의 인자로 들어감

[예제 Code]

Throwable actualException = assertThrows(NullPointerException.class,
                () -> getCryptoCurrency("XRP"));   // (1)

        assertThat(actualException.getCause(), is(equalTo(null)));

3). Custom Matcher

[예제 Code]

public class IsNotANumber extends TypeSafeMatcher {

  @Override 
  public boolean matchesSafely(Double number) { 
    return number.isNaN(); 
  }

  public void describeTo(Description description) { 
    description.appendText("not a number"); 
  }

  public static Matcher notANumber() { 
    return new IsNotANumber(); 
  }

} 
 assertThat(Math.sqrt(-1), is(notANumber())); 

Extra

  1. Hamcrest의 다양한 Matcher

  2. Hamcrest의 Custom Matcher 구현




Slice Testing


0. @애너테이션

  1. SpringApplication

    • ApplicationContext 의 구현체
    • 스프링 컨테이너/ 빈 컨테이너
    • Spring Container/ Bean Container
  2. List

    • List list = new ArrayList<>();
    • 이렇게 생성시 Object 타입으로 받음 <=> List
  3. @SpringBootTest

    • SpringBoot기반의 애플리케이션 테스트를 위한 Application Context를 생성
  1. @AutoConfigureMockMvc

    • API계층에서 테스트하기 위한 애너테이션
    • Controller 테스트에 필요한 어플리케이션 구성이 자동으로 진행됨
    • MockMvc와 같은 기능을 사용하기 위해서 반드시 추가해 줘야한다.
  2. @DataJpaTest

    • 데이터 엑세스 계층을 테스트하기 위한 애너테이션
    • MemberRepository의 기능을 사용하기 위해 Configuration을 설정해주고 @Transactional 애너테이션을
      포함하고 있어 하나의 테스트 실행이 종료되는 시점에 DB저장된 데이터는 롤백처리된다.

1. SliceTest

  1. 슬라이스 테스트

    • 개발자가 구현해 놓은 특정 계층을 잘라서 테스트한것
    • 해당 특정 계층에 대한 테스트에 집중하는 것
    • API계층 : 클라이언트의 요청을 받아들이는 핸들러인 Controller 부분
    • Service계층 : 데이터 엑세스 계층으로, 실제 repository에 저장
  2. Controller 테스트를 위한 클래스 구조 구성

    1). MockMvc

    • Tomcat같은 서버를 실행하지 않고, Controller를 테스트할 수 있는 환경을 지원하는 Spring MVC 테스트 프레임워크

    • 이를 통해 작성한 Controller를 호출해서 손쉽게 테스트 가능

      (1) mockMvc.perfom()

      • 테스트를 위해 perfom() 메서드를 사용하여 Controller의 핸들러 메서드에 요청전송
      • 이후 2~4 의 메서드를 perfom() 메서드 안에서 호출
      • ResultActions 객체로 결과값을 받음

      (2) post()

      • 메서드를 통해 HTTP POST METHOD와 request URL을 설정합니다.

      (3) accept()

      • 메서드를 통해 클라이언트 쪽에서 리턴 받을 응답 데이터 타입으로 JSON 타입을 설정합니다.

      (4) contentType()

      • 메서드를 통해 서버 쪽에서 처리 가능한 Content Type으로 JSON 타입을 설정합니다.

      (5) content()

      • 메서드를 통해 request body 데이터를 설정합니다.

      (6) andExpect(status().isCreated())

      • 입력한 파라미터를 통해 입력한 매처로 예쌍되는 기대 결과 검증
      • ResultActions의 객체에 status 201인지 매치시킴

      (6-1) andExpect(jsonPath("$.data.name").value(post.getName()))

      • jsonPath() 메서드를 통해 response body(JSON 형식)의 각 프로퍼티 중에서 응답으로 전달 받는 name 값이 request body로 전송한 name과 일치하는지 검증

      (7) andReturn();

      • MvcResult 인터페이스로 response 데이터를 확인하고 응답 에이터를 출력할 때 사용

    2). @AutoConfigureMockMvc , @SpringBootTest(SpringbootApplication을 찾아 테스트를 위한 빈들을 생성)

    • 애플리케이션 테스트를 위한 애너테이션

[MemberController 예제 Code]

@SpringBootTest
@AutoConfigureMockMvc
class MemberControllerTest2 {
    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private Gson gson;

    @Test
    void postMemberTest() throws Exception {
        // given
        MemberDto.Post post = new MemberDto.Post("hgd@gmail.com",
                                                        "홍길동",
                                                    "010-1234-5678");
        String content = gson.toJson(post);

        // when
        ResultActions actions =
                mockMvc.perform(
                                    post("/v11/members")
                                        .accept(MediaType.APPLICATION_JSON)
                                        .contentType(MediaType.APPLICATION_JSON)
                                        .content(content)
                                );

        // then
        MvcResult result = actions
                                .andExpect(status().isCreated())
                                .andExpect(jsonPath("$.data.email").value(post.getEmail()))  // (1)
                                .andExpect(jsonPath("$.data.name").value(post.getName()))   // (2)
                                .andExpect(jsonPath("$.data.phone").value(post.getPhone())) // (3)
                                .andReturn();

        System.out.println(result.getResponse().getContentAsString());
    }
}
  1. 데이터 엑세스 계층
    • 데이터 엑세스 테스트 직전 DB상태를 실행 이전으로 되돌려 깨끗이 만듬
    • 각 @Test마다
  1. Gson 라이브러리

    • import com.google.gson.Gson;
    • private Gson gson;

    1). gson.toJson(post)

    • 해당 라이브러리를 통해 JSON으로 변환해준다.

Extra

  1. 스모크 테스트

  2. MockMvc

  3. MockMvc의 request수행 메서드들

  4. MockMvcResultMatchers와 jsonPath()

  5. @DataJpaTest




[Mockito]


0. @애너테이션/정보

  1. RequestBuilder
    • MockMvcRequestBuilders는 RequestBuilder 구현클래스의 팩토리 메서드이다.
    • MockMvcRequestBuilders 는 MockHttpServletRequestBuilder와 MockMultipartHttpServletRequestBuilder을 반환하는 팩토리 클래스이다.
    • MockMvcRequestBuilders 는 MockHttpServletRequestBuilder을 반환하는 팩토리 클래스이다.  
    • MockMvcRequestBuilders는 get,post,put,delete등 요청 방식을 지정 하여 MockHttpServletRequestBuilder 반환

0. @애너테이션

  1. @MockBean

    • Application Context에 등록되어 있는 Bean에 대한 Mockito Mock객체를 생성하고 주입해주는 역할을 한다
    • Mockito Bean객체를 등록해주고, 동일 타입 Bean이 존재하면 MockBean으로 대체해준다.
    • mock객체를 스프링 컨텍스트에 등록하여 @SpringBootTest를 통해서 Autowired에 의존성이 주입
  2. @Mock

    • 추가한 필드변수를 Mock객체로 생성해준다.
    • @InjectMocks에 대해서만 해당 클래스안에서 정의된 객체를 찾아서 의존성을 해결
    • 즉, @InjectMocks 필드변수에 @Mock객체를 주입시켜줌
  3. @ExtendWith(MockitoExtension.class)

    • Spring 사용없이 Junit에서 Mockito 기능을 사용하기 위한 코드
  4. @InjectMocks

    • @Mock객체를 주입받을 객체를 선언

1. Mockito란

  1. Mock 이란

    • 실제 제품처럼 전체적인 기능이 동작하진 않지만, 일부 기능을 테스트해 볼 수 있는 모형제품
    • 테스트에서 Mock객체(가짜 객체)를 사용하여 테스트하는 것을 Mocking이라함
    • 해당 계층만 중점적으로 테스트하기 위하여 Mock객체를 사용
  2. Mockito란

    • Mock객체를 생성하고, 이 객체가 진짜처럼 동작하게 해주는 역할을 하는 라이브러리
    • Spring Framework자체에서 지원하고 있음
  3. Code 리뷰

    1). given(Controller 메서드 변수)

    • Mock객체가 특정 값을 리턴하는 동작을 지정하는데 사용한다.

    2). Mockito.any(Member.class)

    • 실제 메서드의 입력으로 들어가는 클래스 타입

    3). will.return(member)

    • 메서드가 리턴할 stub데이터
    • 해당 Mock 객체가 리턴할 데이터를 지정

[예제 Code]

given(memberService.createMember(Mockito.any(Member.class)))
                .willReturn(member);

Extra

  1. Stubbing

    • Mock객체가 항상 일정한 동작을 지정하는 것
    • 항상 동일한 객체
  2. Mockito 사용법




[TDD]


1. TDD란

  1. TTD

    • '테스트 주도 개발' 로써 테스트를 먼저 하고 그 다음에 구현
    • 모든 조건에 만족하는 테스트를 먼저 진행한 뒤에 조건에 맞지않는 테스트를 단계적으로 실행
    • 'failed'인 테스트 케이스를 지속적으로 수정해나가면서 'passed'가 되도록
    • 너무 많은 양은 코드가 아닌 passed 가 될정도의 코드만 우선 작t
    • 실패하는 테스트 → 실패하는 테스트를 성공할 만큼의 기능 구현 → 성공하는 테스트 → 리팩토링 → 실패하는 테스트와 성공하는 테스트 확인’ 이라는 흐름을 반복
  2. TTD 장/단점

    1) 장점

    • 테스트를 통과 할 만큼의 기능을 구현하므로 한번에 너무 많은 기능을 구현할 필요가 없습니다.

    • 테스트의 코드가 추가되면서 검증하는 범위가 넓어질 수록 기능 구현도 점진적으로 완성되어 갑니다.
      즉, 단순한 기능에서 복잡한 기능으로 확장 되면서 그때 그때 검증을 빼먹지 않고 할 수 있습니다.

    • 리팩토링 할 부분이 눈에 보이면 그때 그때 리팩토링을 빠르게 진행하기 때문에 리팩토링의 비용이 상대적으로 적어집니다.

    • 리팩토링을 통해 꾸준히 코드를 개선하므로 코드의 품질을 일정 부분 유지할 수 있습니다.

    • 코드 수정 이 후, 바로 테스트를 진행할 수 있으므로 코드 수정 결과를 빠르게 피드백 받을 수 있습니다.
      즉, 수정 결과를 그때 그때 확인할 수 있으므로 잘못된 코드가 남아있을 가능성이 상대적으로 줄어듭니다.

    2). 단점

    • 가장 큰 단점은 TDD의 개발 방식이 익숙하지 않다는 것입니다.
    • 테스트 코드의 작성에 익숙하지 않은 사람, 테스트 코드를 작성하길 원치 않는 사람들에게는 부정적인 방식일 수 있습니다.
    • 팀 단위로 개발을 진행해야 하므로 팀원들 간 사전에 협의가 되어야 합니다.

Extra

  1. 애자일 개발 방식
  1. TDD



  1. 피드백 😮
  • 유닛 테스트의 경우, Hamcrest나 Assertion을 통하여 특정 기능(메소드 단위)로 측정하는 방법이다.

  • 슬라이싱 테스트의 경우, Mockito(가짜객체)를 사용하지 않으면 뜻하지 않게 DB쪽을 사용하게 되어 Mockito객체를 사용하여 해당 계층(Layer)만을 테스트 할 수 있음

  1. 앞으로 해야 될 것 😮
  • 매일 꾸준히 할 것
    • 꾸준히 velog 작성
    • Java 언어 및 Algorithm 공부(Coding-Test)
    • 틈틈히 운동 하기

  • 내일 해야 할 것
    • Spring API 문서화 학습
profile
Will be great Backend-developer

0개의 댓글