jtwjs Dev Wiki
  • DEV_ROAD
    • 💪🏻 생존하기
    • Week 1
      • 개발 환경 세팅
      • 타입스크립트
      • 리엑트
      • Testing Library
      • Parcel & ESLint
    • Week 2
      • JSX
      • Virtual DOM
    • Week 3
      • React Component
      • React State
    • Week 4
      • Express
      • Fetch API & CORS
      • React Hook
      • useRef & Custom Hook
    • Week 5
      • TDD
      • React Testing Library
      • MSW
      • Playwrite
      • Snapshot
    • Week 6
      • Separtion of Concerns
      • Principle
      • DI, (Dependency Injection)
      • Reflect-metadata
      • TSyringe
      • External Store
      • Follow Redux
      • usestore-ts
      • useSyncExternalStore
    • Week 7
      • Routing
      • Routes
      • Router
      • Navigation
    • Week 8
      • Design System
      • Style Basics
      • CSS-in-JS
      • Styled-Components
      • Global Style & Theme
    • Week 9
      • 개발하기 전 준비
      • 상품 목록 페이지
      • 상품 상세 페이지
      • 장바구니 페이지
    • Week 10
      • 로그인
      • 로그아웃
      • 회원가입
      • 주문 목록 & 주문 상세
    • Week 11
      • 배송 정보 입력
      • 포트원 결제 요청
      • 배송 및 결제 정보 전달
    • Week 12
      • 관리자 웹사이트개발시작
  • DEV_NOTE
    • TypeScript
      • 기본적 문법
        • Enum
        • 다형성
          • Untitled
        • 구조적 타이핑
        • 제너릭 타입
        • 컨디셔널 타입
        • 함수 메서드 타이핑
        • infer로 타입스크립트의 추론 직접 활용
        • 재귀 타입
        • 템플릿 리터럴 타입
        • 추가적인 타입 검사 satisfies 연산자
        • 타입스크립트 건망증
        • 원시 자료형에도 브랜딩 기법 사용 가능
        • 타입 좁히기
        • 유용한 타입 만들기
        • 데코레이터 함수
        • 앰비언트 선언도 선언 병합이 된다.
        • 앰비언트 선언도 선언병합이 된다.
    • Testing
      • Unit Testing
      • 단위 테스트의 두 분파
      • 좋은 단위 테스트를 구성하는 4대 요소
      • 테스트 대역과 식별할 수 있는 동작
      • 단위 테스트 스타일
      • 가치 있는 단위 테스트를 위한 리팩토링
      • 통합 테스트
      • Cross Browsing Testing
      • 기능 테스트 종류
      • React Testing Pattern
      • 프론트엔드 테스트 입문
        • 테스트 범위
        • 단위 테스트 검증
        • Mock
        • UI 컴포넌트 테스트
        • 테스트 커버리지
        • 웹 통합 테스트
        • MSW
        • 스토리북
        • 시각적 회귀 테스트
        • E2E 테스트
        • Github Actions 설정
        • 깃허브 액션에서 E2E
      • 시프트 레프트
        • 테스트 기본중의 기본
        • 단위 테스트
        • 코드 복잡도
        • 리팩터링
        • 코드 리뷰
        • 통합 테스트 패턴
        • 시스템 테스트의 자동화
        • 탐색적 테스트
      • Test Tip
      • vitest
      • playwright
      • Test Data Generator
      • MSW
    • Algorithm
      • coding test
      • Data Structure
    • Next.js
      • Data Fetching
      • Hydration
      • Next 13
      • Optimization
      • Next 15
    • Tailwind
      • Tailwind CSS
      • Theme
      • Directives
      • Tool
      • Design System
    • Storybook
      • Storybook
      • CSF3
      • CDD
      • Headless Component
    • Funtional Programming
      • 함수형 프로그래밍
      • 참조 투명성
      • 부수효과
      • 함수 합성
      • 제너릭 타입 활용하기
      • 암묵적 입출력
      • 액션과 계산, 데이터
      • 계층형 설계
      • 호출 그래프
      • 함수형 설계
      • 불변성
      • 일급 함수
      • 함수형 도구
    • Git
      • Github Actions
      • Conflict
      • Branch 전략
    • Contents Format
      • Audio
    • 3D Graphic
      • 3D keyword
      • Three.js
      • Geometry
      • Material
      • Light
      • Camera
      • Decal
      • Rotation
      • Text
      • Shadow
      • Fog
      • Post Processing
      • Animation
      • Math
        • Vector Space
        • 벡터의 연산
        • 회전 계산
      • 3D 컨텐츠가 만들어지는 과정
      • R3F
      • Env
      • Scene
      • Transform
      • R3F
      • Interaction & Raycast
      • Rendering Algorithnm
      • Blender
      • Blender
    • Accessibility
      • 접근성이란
    • Interactive Web
      • Parallax
      • Canvas
      • requestAnimationFrame
      • Effect
      • HSL
      • React.js + Canvas
      • Matter.js
    • AWS
      • DevOps
      • Amplify
      • S3
      • 클라우드 컴퓨팅
        • 온프레미스와 클라우드
        • 클라우드 도입효과
        • 클라우드 컴퓨팅의 범위
        • 컴퓨팅 옵션
          • EC2 - Virtual Machin
          • ECS, EKS - Container
          • Lambda - Serverless
        • 네트워크 가상화
        • 스토리지
        • 데이터베이스
        • 데이터 수집
        • 머신 러닝 영역
        • IoT 영역
        • 블록체인 영역
      • 클라우드 아키텍처 설계
    • Network
      • Web Server & WAS
    • System Design
      • System Design
      • Component
      • 의존성을 배제한 개발
      • Error Handling
      • Architecture
        • 모노로틱 아키텍처
        • Clean Architecture
        • Layered Architecture
        • 이벤트 기반 아키텍처
      • 상황을 파악하는 메타인지
      • 중복 문제 해결하기
      • Monorepo Arhitecture
        • 모노레포 운영과 트러블슈팅
        • Module Federation
      • 코드 병목지점
      • API 대응
      • 공통 코드
      • Infra 구축
      • 모듈 기반의 개발 방식
      • Design System
        • 최소 수준의 아키텍처 설정
        • 더 효율적인 디자인시스템 만들기
        • 디자인 시스템과 UI 라이브러리 목적
        • 디자인 토큰
      • 효율적인 업무
        • 업무 프로세스 병목 파악
      • Clean Code
      • Design Pattern
        • CQRS Pattern
        • Strangler Fig Pattern
        • 데코레이터 패턴
        • 커맨드 패턴
        • 전략 패턴
        • 옵저버 패턴
      • A/B 테스팅
      • 대규모 리엑트 웹앱 개발
        • 복잡성 관리
        • 모듈성
        • 성능
        • 디자인 시스템
        • 데이터 패칭
        • 상태 관리
        • 국제화
        • 코드 조직화하기
        • 개인화 A/B 테스팅
        • 확장 가능한 웹 아키텍처
        • 테스팅
        • 툴링
        • 기술적 마이그레이션
        • 타입스크립트
        • 라우팅
        • 사용자 중심 API 디자인
        • 리액트 미래
    • Performance
      • React DevTools
      • Component 최적화
      • Page Load
      • API
    • MFA
      • MSA
      • MFA 도입하기
      • Monorepo
        • Monorepo Tool
        • Yarn Berry Workspace
        • Turborepo
      • MFA Composition
      • SPA 통합
      • Design System
      • Package Manager
        • Yarn
        • pnpm
      • Transpiler & Bundler
        • Babel
        • Rollup
        • esbuild
        • swc
        • Webpack
        • Vite
      • 분해와 통합을 위한 여러 기술 비교
    • State Management
      • Zustand
    • React v18
      • Automatic batching
      • Suspense
      • Transition
    • SEO
      • Search Engine Optimization
      • Open Graph Element
      • Metadata
    • FE Develop
      • Scrubbing
      • Clipboard
    • Refactoring
      • 리팩토링 깊게 들여다보기
      • 긴 코드 조각내기
      • 타입 코드 처리하기
      • 유사한 코드 융합하기
      • 데이터 보호
      • 코드 추가 및 제거
    • OAuth 2.0
    • Analytics
      • Mixpanel
    • ETC
      • VSCode
    • React Hook In Action
      • useContext & Provider
      • 커스텀 훅
      • 코드 분할하기 with Suspense, lazy
      • Suspense와 이미지 적재하기
      • useTransition, uesDeferredValue
      • SuspenseList
Powered by GitBook
On this page
  • 테스트 실행 숏컷
  • VSCode Extension
  • 올바른 테스트 작성법
  • 인터페이스를 기준으로 테스트 작성하기
  • 100% 커버리지보다는 의미 있는 테스트인지 고민
  • 가독성 있는 테스트 코드 작성법
  • 좋은 테스트 구조
  • 준비 (Arrange, Given)
  • 실행 (Act, When)
  • 검증 (Assert, Then)
  • 좋은 테스트 원칙
  • First 원칙
  • 좋은 테스트 범위
  • 좋은 테스트 조건
  • 무엇을 중요하게 생각해야 하나?
  • Tradeoffs
  • React Testing Library
  1. DEV_NOTE
  2. Testing

Test Tip

Previous탐색적 테스트Nextvitest

Last updated 3 months ago

테스트 실행 숏컷

  • vscode >Keyboard Shorcuts > test.run > run test in current file or run test at cursor

VSCode Extension

    • ID: vitest.explorer

    • ID: kingwl.vscode-vitest-runner

    • ID: ms-playwright.playwright

올바른 테스트 작성법

인터페이스를 기준으로 테스트 작성하기

  • 내부 구현에 대한 테스트 코드는 강한 의존성 때문에 깨지기 쉽고 유지보수 하기 어렵다.

  • 인터페이스를 기준으로 캡슐화에 위반되지 않으며 종속성이 없는 테스트를 작성하자

100% 커버리지보다는 의미 있는 테스트인지 고민

  • 커버리지를 쫓다보면 큰 유지 보수 비용이 발생하며 제데로 된 검증이 되었다는 착각이 들수 있다.

  • 의미있는 테스트가 무엇인지 고민해보자

가독성 있는 테스트 코드 작성법

1. 테스트 하고자 하는 내용을 명확히 작성하기

테스트 디스크립션을 상세히 명확히 작성하는것이 앱의 동작을 이해하는데 도움된다.

// 검증 대상 - 리스트에서 체크된 항목들 제외

// BAD
it('리스트에서 항목이 제데로 삭제된다.', () => {})

// GOOD
it('항목들을 체크한 후 삭제 버튼을 누르면 리스트에서 체크된 항목들이 삭제된다.', () => {})

2. 하나의 테스트에는 가급적 하나의 동작만 검증하자

테스트 코드에서도 검증 범위의 책임을 나누는것이 중요하다.

  • 간단한 테스트는 상관없지만, 다양한 컴포넌트들이 조합되었을 때 시나리오를 검증해야 한다면 하나의 테스트에서 한번에 검증하는것이 아닌 여러 개로 나누어 검증하는 것이 가독성과 유지보수성이 높다.

  • 검증에 필요한 코드들이 많아지고 디스크립션도 장황해진다.

  • 여러 개를 검증하다 보니 특정 로직이 수정되어도 테스트 자체가 깨지게 된다.

  • 어떤 동작에서 테스트가 실패했는지 파악하기 어려울 수 있다.

  • 검증의 책임이 명확히 나눠져 있기 때문에 이런 테스트 코드는 유지보수하기 쉽다.

// BAD
it('장바구니를 담긴 상품들이 정상적으로 노출되고, 수량을 변경하면 가격이 재계산 된다. 그리고 삭제 버튼을 누르면 상품이 삭제된다.', 
  () => {}
 )
 
 // GOOD
it('장바구니에 담긴 상품들을 정상적으로 렌더링 한다.', () => {})
it('장바구니에 담긴 상품의 수량을 수정하면 가격이 재계산 된다.', () => {})
it('장바구니에 담긴 항목의 삭제 버튼을 누르면 리스트에서 삭제된다', () => {})

단일 책임 원칙 (SRP, Single Responsibility Principle)

  • 모든 클래스는 하나의 책임을 갖고, 그와 관련된 책임을 캡슐화하여 변경에 견고한 코드를 만들어야 한다.

좋은 테스트 구조

테스트는 독립적인 테스트 케이스가 있고, 테스트 전후에 실행되는 것들(Before**, After**)이 있다.

beforeAll(() => {})
beforeEach(() => {})
afterEach(() => {})
afterAll(() => {})

it('...', () => {
  // Arrange, Given
  const productService = new ProductService(new StubProductService());
  
  // Act, When
  const items = await productService.fetchAvailableItems();
  
  // Assert, Then
  expect(items.length)toBe(1);
  expect(items).toEqual([{ item: 'A', available: true }])
})

준비 (Arrange, Given)

테스트를 위한 object를 생성하고 여러가지 데이터를 준비하는 과정

  • 준비하는 단계에서는 준비과정을 여러개의 테스트에 걸쳐서 반복해서 사용한다면 재사용할 수 있도록 유틸리티 함수로 정의해서 사용

실행 (Act, When)

테스트하고자 하는 코드를 실행

  • 코드를 실행했을 때 의도적으로 실패해보기

  • expect에 다른값을 넣는다든지, 실패하도록 만든 뒤에 실패했을 때 실패하지 않기 위해서 코드를 어떻게 수정해야하는지 확인해보기

  • 버그를 수정할 때 실패하는 테스트를 먼저 만든 후, 버그가 이런 상황에서 발생하구나를 검증한 다음에 버그를 수정해서 이 테스트 코드가 성공할 수 있도록 만드는것이 중요하다.

    • 실패하지 않는 테스트는 필요 없다.

    • 모든 테스트 코드는 의도적으로 실패할 수 있어야 한다.

검증 (Assert, Then)

실행한 코드를 우리가 예상한값과 같은지 검증

  • 내가 하나의 테스트 함수 안에서 검사하는것이 많다면, 여러개의 테스트로 분리할수 없는지 고민해보자.

좋은 테스트 원칙

First 원칙

  • Fast: 느린것에 대한 의존성 낮추기,

    • 테스트가 빠르게 수행되어야지 몇개의 테스트를 가지고 있더라도 빈번히 테스트를 수행해서 문제가 없는지 검증하는것이 중요

  • Isolated: 최소한의 유닛으로 검증하기, 독립적이고 집중적으로 유지

    • 하나의 테스트에서 너무 많은것을 동시에 테스트해서 어디서 어떤것이 잘못되어 테스트가 실패했는지 잘 모르게 작성하지 말고 최소한의 단위로 어디서 실패했는지 한눈에 알 수 있도록 독립적으로 작성

  • Repeatable: 반복이 가능하도록 만들어라, 테스트코드가 실행될 때마다 동일한 결과 유지

    • 언제, 몇번 실행하냐에 따라서 다른 결과를 제공한다면 나쁜 테스트 코드

    • 다른 테스트 코드에 의존하거나, 외부적 환경(네트워크, 디비)에 의존하는 테스트는 불안정

    • 환경에 영향을 받지 않도록.. 작성

  • Self-Validating: Assert library(expect, tobe 등)을 사용하면 스스로 결과를 검증 가능

    • 우리가 사용한 테스트 라이브러리는 기본으로 제공

    • 자동화를 통한 검증단계 도입(CI/CD)

      • 새로운 기능을 추가할 때 기존 테스트 코드에 영향을 주는지 확인하지 않으면 테스트 코드는 불필요

  • Timely: 시기적절하게 테스트 코드 작성

    • 코드를 추가하거나 기능이 수정되고 나서 사용자에게 배포된 후 테스트 코드 작성하는건 의미가 없다.

    • 코드 작성할 때, 리팩토링 전, 사용자에게 배포하기 전에 시기적절하게 테스트 코드 작성해서 예상하지 못한 문제를 빠르게 잡아내는것이 중요

좋은 테스트 범위

Right-BICEP, 모든 요구사항이 정상 동작하는지 확인

  • Boundary conditions: 모든 코너케이스에 대해 테스트하기

    • ex: 잘못된 포맷의 인풋, null, 특수문자, 잘못된 이메일, 작은 숫자, 중복, 순서가 맞지 않는 경우

  • Inverse relationship: 역관계를 적용해서 결과값을 확인

    • 일관성을 유지 (덧셈 -> 뺼셈, 추가 -> 제거)

  • Cross-check: 다른 수단을 이용해서 결과값이 맞는지 확인

    • ex: 추가된 과일 갯수를 구하는 함수를 테스트 한다면, 결과값이 맞는지 확인하기 위해 전체 과일 갯수 - 예전 과일 갯수 값과 동일해야 한다

    • 특정 알고리즘을 구현했다면 정확하게 동작하는지 확인하기 위해서 동일한 알고리즘을 구현한 다른 라이브러리를 이용해서 우리 알고리즘과 라이브러리 알고리즘 결과값이 똑같아야 한다.

  • Error conditions: 불행한 경로에 대해 우아하게 처리 하는가?

    • 네트워크 에러, 메모리 부족, 데이터베이스 중지 등

    • 예상할 수 있는 모든 에러 케이스에 대해서 테스트가 통과하는지

  • Performance chracteristic: 성능 확인은 테스트를 통해 정확한 수치로 확인

    • 성능 개선의 척도와 확인도 데이터를 통해 확인

좋은 테스트 조건

테스트도 각각 상황 및 조건에 맞게 어떤 결과값을 예상하는지를 테스트하는것이 중요 -> CORRECT 원칙

단순히 하나의 사실에 의거해서 테스트를 작성하는게 아닌, 여러가지 조건들을 테스트해서 테스트 코드도 꼼꼼히 작성

CORRECT 원칙은 테스트 코드 뿐만 아니라 베이스 코드를 작성할 때도 유념

  • Conformance: 특정 포맷을 준수

    • 전화번호, 이메일, 아이디, 파일 확장자 등

    • 인풋이 포맷에 적합할 때, 적합하지 않을 때, 우리 코드가 어떤식으로 동작하는지 예상하는 테스트 코드를 작성

  • Ordering: 순서 조건 확인하기

    • 순서가 중요한 경우

    • 테스트 코드가 배열의 순서를 중요하게 생각하는 코드라면, 순서가 잘못되었을 때 코드가 어떻게 반응할것인지 예상하는것들도 모두 테스트로 나타내야함

  • Range: 숫자의 범위

    • 제한된 범위보다 크거나 작은 경우

  • Reference: 외부 의존성 유무, 특정한 조건의 유무

    • ~일때, ~가 되었을 때, 어떤 특정한 상황/상태 일경우 이런 동작을 한다.

  • Existence: 값이 존재하지 않을 때 어떻게 동작?

    • null, undefined, 0, ''

  • Cardinality: 0-1-N 법칙에 따라 검증

    • 하나도 없을 때, 하나만 있을 때, 여러개 있을 때

  • Time: 상대, 절대, 동시에 일들

    • 순서가 맞지 않은 경우, 소비한 시간, 지역 시간

    • 순서가 맞지 않을 때, 특정 시간을 지나치게 소비했을 때, 시간을 검사한다면 지역마다 나라마다 시간이 달라지는 경우 코드가 어떻게 동작하는지 검사

여러가지 조건에 맞게 테스트를 작성하다 보면 베이스 코드에 놓친 부분을 파악하는 상황이 자주 생김!

무엇을 중요하게 생각해야 하나?

  • itteration speed vs Realistic envirionment

    • itteration speed: 특정한 일을 시작해서 끝날때까지 걸리는 속도

      • 즉, 기능 구현후 테스트 코드 작성하고 검증하는 단계를 한바퀴 돌아오는 속도

      • 코드 수정 후 기존 테스트를 동작해서 결과 확인해서 완성하는 한바퀴를 iteration이라 한다.

      • 테스트 작성하고 테스트 동작하는 속도가 빠르면 빠를 수록 iteration speed는 빠르다고 볼 수 있다.

    • Realistic envirionment: 사용자가 실제로 앱을 사용했을 때의 환경과 최대한 가까운것

"테스트 도구를 선택할 때는 itteration speed와 Realistic envirionment이라는 몇 가지 절충점을 고려하는 것이 좋습니다: 일부 도구는 변경 후 결과 확인까지 매우 빠른 피드백 루프를 제공하지만 브라우저 동작을 정확하게 모델링하지 못합니다. 다른 도구는 실제 브라우저 환경을 사용하지만 반복 속도가 느리고 CI 서버에서 더 취약(flaky) 할 수 있습니다"

테스트에서 제일 필수적인 조건으로 "동일한 인풋이 주어졌을 때 동일한 결과를 도출해야한다" 인데 환경에 따라 결과가 달라지면 최악의 테스트...그래서 E2E 테스트는 실제 브라우저 환경에서 동작하도록 해서 좋은 테스트이지만 itteration speed가 떨어지고 간혹 테스트가 실패하는 문제점이 있다.

  • How much to Mock

    • 얼마만큼 Mock을 할것인가

    • 컴포넌트를 테스트하는 경우 유닛인지 통합인지 경계가 불명확하다.

    • 어디까지 유닛테스트로 부르고, 어디까지 통합 테스트로 부를것인지는 사실 중요하지 않다.

      • util, api, 단일 컴포넌트는 유닛 테스트, 다른 컴포넌트 또는 외부 리소스를 사용한다면 통합 테스트 ㅇㅇ

"컴포넌트의 경우 '단위' 테스트와 '통합' 테스트의 구분이 모호할 수 있습니다. 폼을 테스트하는 경우 해당 테스트에서 폼 내부의 버튼도 테스트해야 할까요? 아니면 버튼 컴포넌트에 자체 테스트 스위트가 있어야 할까요? 버튼을 리팩토링하면 양식 테스트가 중단될 수 있을까요? 팀과 제품에 따라 다른 답이 나올 수 있습니다."

React Testing Library

  • 조금 더 테스트를 간편하게 만들어주고, 테스트 자체만으로 유지보수성을 높혀줌

  • 리액트 컴포넌트를 내부 구현사항에 의존하지 않고 테스트를 간편하게 만들어줌

    • 리엑트 컴포넌트를 테스트할 때 내부적으로 어떤 CSS를 사용하는지, 어떤 코드를 가지고 있는지 구현사항에 의존하지 않고 화면상에, 사용자 입장에서 '특정 텍스트를 갖는 버튼을 가져와서 클릭하거나, 텍스트가 보이는지 안보이는지' 조금 더 외부적인 사용자 관점에서 테스트를 작성할 수 있도록 도와줌

    • 이런 접근법은 리팩토링을 쉽게 만들어주고, 웹 접근성과 코드를 작성하는데 좋은 원칙을 따라갈 수 있게 도와줌

    • 검증하려는 기능과 관계없는 코드가 수정되도 테스트가 실패하는 상황을 만들지 말자

  • 단점으로 RTL 자체로는 자식 노드들을 가볍게 렌더링하는건 제공하지 않음

    • 컴포넌트 하나만 테스트하더라도 내부 컴포넌트들을 모두 렌더링하게됨

    • 내부 컴포넌트들이 갖는 의존성까지 모두 설정해줘야해서 까다로움

      • jest/vitest 의 mock 활용

"React 테스트 라이브러리는 구현 세부 사항에 의존하지 않고도 React 컴포넌트를 테스트할 수 있도록 도와주는 헬퍼 세트입니다. 이 접근 방식을 사용하면 리팩토링이 쉬워지고 접근성을 위한 모범 사례로 나아갈 수 있습니다. 자식 없이 컴포넌트를 '얕게' 렌더링하는 방법은 제공하지 않지만, Jest와 같은 테스트 러너를 사용하면 모킹을 통해 이를 수행할 수 있습니다."

Vitest
Vitest Runner
Playwright Test for VSCode
Tradeoffs