Skip to content

Latest commit

 

History

History
157 lines (100 loc) · 8.06 KB

스프링캠프2019_무엇을테스트할것인가.md

File metadata and controls

157 lines (100 loc) · 8.06 KB

[스프링캠프 2019] 무엇을 테스트할 것인가? 어떻게 테스트할 것인가? (by 권용근)

테스트로 부터 얻을 수 있는것

  • 안정감과 자신감
  • 대상은? 현재와 미래의 나, 동료

무엇을 테스트할 것인가?

로또 구현 예시

  • 요구사항
    • 6개의 숫자를 반환, 중복되지 않는 숫자, 랜덤하게 반환

image-20230122161810706

  • 질문) 중복에 대해서도 테스트 되어야 하지 않을까요?
    • 대답) 저는 Set 으로 구현해서 중복이 나올 수 가 없어요
    • 위는 잘못됐다. 왜냐면 구현은 언젠가 어떻게든 변할 수 있기때문이다. (미래의 내가 망각할 수도, 미래 동료가 바꿀수도) -> 구현이 아닌 비즈니스 설계사항을 테스트해야지 안정감, 자신감을 얻을 수 있다

구현테스트

  • 만약 private 메서드를 public 메서드로 바꿔서 테스트하고자하면 구현테스트를 하고 있을 가능성이 매우 큼
  • 이럴경우에는 그 행위를 가지고 있는 안쪽의 비즈니스 테스트만 더해질 안쪽을 들여다볼 필요는없다. 만약 추가하는게 어색하다면 private 메서드가 안쪽에 있는게 이상한걸수도 ..
    • image-20230122163314932

즉 요약하면, 우리가 구현이 아니라, 설계를 테스트해야한다.

Non Testable

  • "제어할 수 없는 영역"

  • 예시

    • Radndom, Shuffle, LocalDate.now()
    • 외부 세계
      • HTTP
      • 외부 저장소
  • 왜 테스트하기 힘든지?

    • 테스트는 고정되어있는데 그사이에 외부세계는 변화될 수 있다. 그럴때 테스트가 실패하게된다.
    • 제어할 수 없는 영역은 멱등한 결과를 보장할 수 없다.
  • 로또 예제

    • 위 요구사항 중 "랜덤하게 반환"중 랜덤은 제어할 수 없으므로 테스트 불가능한것이다.
    • 요구사항을 잘못 도출했을 가능성있음. 따라서 "랜덤하게 반환"을 "의도한 전략대로 반환"으로 변등 다르게 풀어서 테스트하기 쉽게 만들 수 있다.
      • 아래는 전략패턴으로 리팩터링함 (테스트 가능하게됨)
      • image-20230122165141750

"항상 성공할 수 있는것 항상 동일한 결과가 나올 수 있는 것을 테스트"

여기까지 요약

  • 무엇을 테스트할것인가?
    • 설계를 테스트하자.
    • 항상 동일한 결과가 나올 수 있는것을 테스트하자.

어떻게 테스트할 것인가?

1. 테스트 가능한 것, 불가능한 것

테스트할 수 없는 메서드는 테트할 수 있는 메서드까지 오염을 시킨다.

배달팁 계산 예제

![image-20230122170934084](../../../Library/Application Support/typora-user-images/image-20230122170934084.png)

![image-20230122170955938](../../../Library/Application Support/typora-user-images/image-20230122170955938.png)

image-20230122171412500

  • 여기서 가장 안쪽 있는 메서드에 LocalDateTime.now()는 제어할 수 없는 영역이 있다. -> 결국 전체가 테스트할 수 없는 코드로 오염이된다.

image-20230122171716069

image-20230122171840720

  • isValid를 인수를 받을 수 있게 변경한다. 넘겨진 시간에 의해 제어가 되므로 테스트할 수 있게된다.

![image-20230122172936719](../../../Library/Application Support/typora-user-images/image-20230122172936719.png)

  • 계속 올린다. 어디까지 올릴것인가?
    • 여기서 Boundary Layer란? 한 모듈로서의 의미를 지니는 가장 바깥 쪽 (이건 설계에 따라 딱 정의하기 힘든 부분)
    • ![image-20230122173144723](../../../Library/Application Support/typora-user-images/image-20230122173144723.png)

"테스트 불가능한 영역을 Boundary Layer로 올려서 테스트 가능하도록 변경"

2. Java, Spring Framework

  • @SpringBootTest
    • image-20230122221549355
    • Spring Context가 느리기 때문에 필요없다.
      • 빠른 피드백을 받을 수 없다.
    • Spring Context는 언어의 본질을 망각을 시키게할 수 있다.

"Context, Framework 종속적이지 않은 테스트 우선"

3. Test Double

Test Double은 테스트 할 수 없는 영역에 대한 외부 요인을 부여할 수 있도록 도와줌

  • 무엇을 Test Double로 처리해야할까?

    • image-20230122223436601

      위가 call stack 최상단이며 1, 2, 3, 4번 코드를 순차적으로 실행 (4번이 테스트 하고싶은 영역)

    • image-20230122223543041

      • 4번만 테스트 할꺼니 제외한 영역을 Test Double로?
    • Test Double을 사용하는 것은 테스트가 구현을 알아야 한다는것

      • ex) 반환 타입 or 입력값 변경하면 오류가 발생한다.
    • Test Double의 남용은 구현 테스트로 유도할 수 있다.

  • 그럼 Test Double을 어떻게 쓰지?

    • Boundary Context 까지 끌어올렸는데 테스트할 수 없는 영역이 존재할때
    • 편리하지만 사용을 의식하여 최소화
    • 순수 자바 어플리케이션으로는 테스트할 수 없는 것
      • -> 대부분 Embedded로 가능
      • ex) 저장소 입출력검증(쿼리 잘나가는지), SPEC 검증(내부 controller, 외부 API)

4. Embedded

  • 종류

image-20230122225913990

  • 테스트와 Embedded 시스템은 동일한 라이프 사이클을 갖도록 구성
    • image-20230122230255490

image-20230122230321361

5. EndPoint Test

  • Spring Framework Support
    • MockMvc, REST Assured, WebTestClient
  • 테스트의 목적은 요청과 응답 스팩 검증만으로 제한하는게 정신 건강에 좋을것이라고 생각 (뒤를 아예 모르게)
    • image-20230122230943413
    • 뒤에 로직을 다 알아야되고, 테스트 가능한 영역이여야하고, case by case도 너무 많기에

TIP & RULE

  1. 테스트간 서로 영향을 미치지 않도록 상호 독립적으로 작성
    1. 모든 테스트의 순서와 관계를 생각하여 테스트를 작성하기 어려움
    2. 공유되는 자원은 초기화하여 다른 테스트에 영향을 받지 않도록
    3. 단계가 필요하다면 JUnit5 의 Dynamic Testfㅡㄹ 추천
  2. 테스트 안에 의도와 목적이 들어나도록 작성
    1. 어떤 조건에서, 무엇을 수행했을때, 어떤 결과가 나올지 알 수 있어야한다. (개인적으로 @Before에 적으면 파악하기 힘듬)
    2. 테스트 코드 역시 가독이 중요
  3. 테스트 코드도 리팩토링 대상
    1. 코드양에 대한 이야기가 아닌 가 안정성, 요구사항 정리등 비즈니스 코드와 동일한 수준의 리팩토링이 함께 이루어져야 한다.

Reference