테스트 최적화

해피데빙·2023년 3월 20일
0

카카오 vue 컴포넌트 테스트

-> 이 사람 자료 겁나 잘 만든다. 보고 배우기

  • given
  • when
  • then

컴포넌트 생성 조건 셋팅
페이지 진입 시 라우트에서 아이디 정보를 가져오도록 되어 있는데 정보 가져오지 못하면 에러 모달 띄워주는 상황 테스트

  • 적절한 설계 기법
  • 공통 테스트 케이스 재사용
  • 테스트 케이스 구조 관리
  • 테스트 케이스 적절히 분할

제어할 수 없는 값에 의존하는 코드들을 최대한 줄이기

  • 제어할 수 없는 값: 함수의 인자로 받도록, 최대한 진입점에 위치, 함수 기본값 혹은 의존성 주입 통해 해결
  1. 구현 대신 기능을 검증한다.
    변하는 것은 구현.
    기능을 검증하면 테스트가 깨지지 않는다.
    기능이 변경되면 테스트 코드를 먼저 수정하고 깨진 테스트를 고치기 위해 구현 코드를 수정한다.
  2. mock 사용을 ‘지양’한다.
    외부와 분리할 수 있다는 장점이 있지만 구현과 강하게 결합되어 있기 때문에 깨지기 쉽다.
  3. 테스트 케이스만 보고 테스트를 이해할 수 있도록 작성한다.
    테스트 케이스간 공유하는 객체를 만들지 않는다.
    대신 함수로 만들자.
    전후 맥락이 필요한 테스트 케이스를 만들지 않는다.
    제품 코드에서 export 되는 상수를 참조하여 작성하지 말자.
  4. 상태를 테스트한다.
    상호작용 대신 상태를 테스트 한다.
    상태를 테스트하는 것보다 출력을 테스트하는 것이 더 좋

출처 : Line에서 테스트를 최적화하는 방법

테스트 설계 방식

  1. 각자의 경험에 기반해서 만드는 방식
  • 테스터가 보유한 시스템, 도메인에 대한 지식에 의존
  • 테스터의 테스트 케이스 작성 스킬에 의존
  1. 테스트 설계 기법에 기반한 방식
  • 더 많은 사람들의 경험 집약
  • 더 많은 도메인, 프로젝트에 적용 가능
  • 같은 노력으로 더 큰 영역 커버 (효율적)

설계 방법

  1. ep-bva
    동등 분할 기법
  • 프로그램의 입력값과 출력값이 특정 그룹으로 돼 있고
  • 분류된 그룹의 값들을 시스템에서 동일하게 취급
  • 경곗값 분석 기법으로 동등 분할의 확장 형태
  1. pairwise : 가능한 모든 입력값의 조합을 테스트하는 대신 짝들의 조합으로 테스트하는 방법입니다.
  2. 상태 전이 : 보유한 테스트 역량으로 감당하기 어려운 긴 비즈니스 흐름이나 이벤트 배열을 테스트할 때 사용하는 기법인데요
  • 사실 좋은 테스트 케이스를 만들려면 설계하는 것만으로는 충분하지 않습니다.

설계하는 것과 더불어

1) 공통 테스트 케이스를 만들어 재사용
ex. 푸시 알림에 대한 일반적인 테스트 케이스 정리
: 새로운 프로젝트에서 푸시 알림에 대해 테스트할 필요가 있으면 이 테스트 케이스 사용

2) 테스트 케이스를 구조화 : 리뷰 시간 절약, 테스트 케이스 업데이트 시간 감소
ex. UI - 주요 기능 - UX / 네비게이션 등 특정 요구 사항 - 경험에 비춰볼 때 버그 가능한 부분
=> 전체적으로 설계 구조, 아이디어 더 잘 이해할 수 있어 리뷰 시간, 업데이트 시간 단축 가능

3) 테스트 케이스를 분할 : 시간 절약 위해 작은 테스트 케이스를 큰 테스트 케이스에 결합하는 경우 작은 단계의 실패가 전체 테스트의 실팰로 이어짐. (매번 다 테스트해야 함). 회귀 테스트 진행 시 실제 위험이 되는 건 몇몇 단계일 뿐일 때도 긴 테스트 케이스 전부 실행해야 할 수도

정리

  • 적절한 설계 기법
  • 공통 테스트 케이스 재사용
  • 테스트 케이스 구조 관리
  • 테스트 케이스 적절히 분할

설계 기법

  1. EP-BVA: EP는 동등 분할(Equivalence Partitioning) 기법으로 프로그램의 입력값과 출력값이 특정 그룹으로 돼 있고 분류된 그룹의 값들을 시스템에서 동일하게 취급한다는 특성을 이용한 테스트 기법입니다. BVA는 경곗값 분석(Boundary Value Analysis) 기법으로 동등 분할의 확장 형태입니다.
  2. 페어와이즈(pairwise): 가능한 모든 입력값의 조합을 테스트하는 대신 짝들의 조합으로 테스트하는 방법입니다.
  3. 상태 전이(state transition): 어떤 이벤트가 발생했을 때 테스트 대상이 다른 상태로 전이되는 경우의 수를 테스트하는 방법입니다.

좋은 테스트 코드란

출처 : 테스트하기 좋은 코드 - 테스트하기 어려운 코드

경험상 몇번을 수행해도 항상 같은 결과가 반환되는 함수 (멱등성이 보장되는 순수함수) 가 테스트하기 좋은 코드였다.

최대한 피해야만 하는 두가지 요소

1. 제어할 수 없는 값에 의존하는 경우

- Random(), new Date() (LocalDate.now()) 와 같이 실행할때마다 결과가 다른 함수에 의존하는 경우
- readLine 혹은 inputBox 등 사용자들의 입력에 의존하는 경우
- 전역 함수, 전역 변수 등에 의존하는 경우
- PG사 라이브러리등 외부 SDK에 의존하는 경우

전역, 실행 시 결과 다른 함수, 외부 sdk, 사용자 입력
이런 경우 mocking을 해야 수행 가능, 쉽지 않음

ex. 도메인 로직의 경우

2. 외부에 영향을 주는 코드

- console.log, System.out.println() 과 같은 표준 출력
- Logger 등을 사용하는 경우
- 이메일 발송, 메세지큐 등 외부로의 메세지 발송
- 데이터베이스 등에 의존하는 경우
- 외부 API에 의존하는 경우

해결 방법

테스트하기 어려운 코드와 테스트하기 쉬운 코드를 분리해야만 한다.
둘을 분리해서, 테스트 하기 어려운 코드에 오염되는 영역을 최소화하는 것이 중요하다.

테스트의 어려움은 전파된다

가장 쉬운 방법은 생성자, 함수(메소드)의 인자로 테스트하기 어려운 코드의 결과를 받는 것이다.
이번 같은 경우 Order.discountWith() 에서 제어할 수 없는 값을 외부에서 주입받도록 한다.

export default class Order {
    ...
    discount() {
        const now = LocalDateTime.now(); //안에 있던 것
        if (now.dayOfWeek() == DayOfWeek.SUNDAY) {
            this._amount = this._amount * 0.9
        }
    }
}
export default class Order {
    ...
    // 현재시간(now)를 밖에서 주입받도록 한다.
    discountWith(now: LocalDateTimw) { //인자로 받도록
        if (now.dayOfWeek() == DayOfWeek.SUNDAY) {
            this._amount = this._amount * 0.9
        }
    }
}

물론 이렇게 할 경우 함수의 인자가 너무 많아지는 것은 아닌지 부담스럽게 느낄 수 있다.

그럴때는 몇가지 해결책이 있는데,

  1. 같은 Context 라고 생각되는 함수 인자들은 Dto 패턴 으로 묶어서 전달하거나
  2. 의존성 주입을 통해 제어할 수 없는 값을 다루는 객체들은 생성자 주입을 받아서 사용하거나
  3. (언어에 따라 다르지만) 함수 인자의 기본값을 사용하는 방법이 있다 ex. typescript
  4. 기본값을 줄 수 없으면 가장 의존성이 낮은 곳까지 밀어내서 사용
    4-1) 가장 바깥쪽으로 밀어내기
    4-2) 의존성 주입

정리

제어할 수 없는 값에 의존하는 코드들을 최대한 줄이기

  • 제어할 수 없는 값: 함수의 인자로 받도록, 최대한 진입점에 위치, 함수 기본값 혹은 의존성 주입 통해 해결

✅ jest-webpack : webpack 다음 jest
✅ emoji.test.js : vue router test
✅ bottomsheet.js 테스트

profile
노션 : https://garrulous-gander-3f2.notion.site/c488d337791c4c4cb6d93cb9fcc26f17

0개의 댓글