[ 코드잇 스프린트 ] 교육기간 6일차 TIL

2024. 11. 15. 23:30·교육/코드잇 스프린트 : 단기심화 5기


강의 목표

강의시간에 학습한 내용을 정리합니다.

 

강의 내용 정리

강의시간에 학습한 내용을 정리합니다.

 

테스팅의 필요성

  • 테스트는 왜 필요한가?
    • 코드 작성시 버그를 줄여줌
      • 버그 발생 시, 이런 테스트가 있었더라면 발생한 버그를 미리 잡을 수 있었을 테스트를 새로 작성함으로써 수정
    • 기존 코드를 변경(리팩토링)할 때 기존에 동작하던 것들이 고장나지 않는 것을 보장해줌
      • 주요 동작들에 대해 주요한 시나리오는 전부 테스트해야
    • 안심하고 새로운 버전을 빠르게/자주 출시할 수 있게 도와줌
      • main 브랜치 테스트가 모두 통과되면 good to go

 

테스트 설계: 무엇을, 어떻게

  • 테스트 대상
    • 유닛 테스트 : 하나의 기능 단위 ⇒ 하나의 함수, 하나의 클래스
    • 통합 테스트(integration test) : 여러 유닛들이 함께 사용될 때 기대한 대로 동작하는지를 테스트
    • e2e 테스트(end to end) : 특정 유저 시나리오에 대해 실제 프로덕션과 동일한 환경에서 진행하는 테스트
  • 테스트 설계: 유닛 테스트
    • 고정돼있는(미리 정해져 있는) input들에 대해 기대되는 output 값이 리턴되는지 확인
    • 내부 구현을 모른다고 가정하고 input/output으로 테스트(블랙박스 테스팅)
    • 일반적인 경우, 중요한 엣지 케이스들을 전부 커버할 것
    • 유틸 함수/클래스에 대해서는 반드시 작성할 것
  • 테스트 설계: 통합 테스트
    • AAA 패턴 : Arrange, Act, Assert
      • 예시) 버튼을 화면에 띄우고, 버튼을 클릭하면, XX 상태가 된다.
    • Arrange : 버튼이 렌더링되고, 버튼을 클릭할 수 있게 선택한다.
    • Act : 선택한 버튼을 클릭한다.
    • Assert : 기대했던 상태로 변경되었는지 검증한다.

 

실습 : 간단한 테스트 작성

  • tailwind next.js 프로젝트 계속 사용
    • root 위치에 /utils 디렉토리 생성
    • next.js jest 셋업하고, ts-node도 설치(npm i -D ts-node)
    • 예제 테스트 코드와 jest 레퍼런스 참조해서 테스트 코드 완성

정답 코드

import slice from "./slice";

describe("slice", () => {
  const array = [1, 2, 3];

  it("should use a default `start` of `0` and a default `end` of `length`", () => {
    const actual = slice(array);
    expect(actual).toEqual(array);
    expect(actual).not.toBe(array);
  });

  it("should work with a positive `start`", () => {
    expect(slice(array, 1)).toEqual([2, 3]);
    expect(slice(array, 1, 3)).toEqual([2, 3]);
  });

  it.each([3, 4, 2 ** 32, Infinity])(
    "should work with a `start` >= `length`",
    (start) => {
      expect(slice(array, start)).toEqual([]);
    }
  );

  it("should work with a negative `start`", () => {
    expect(slice(array, -1)).toEqual([3]);
  });

  it.each([-3, -4, -Infinity])(
    "should work with a negative `start` <= negative `length`",
    (start) => {
      expect(slice(array, start)).toEqual(array);
    }
  );

  it.each([2, 3])("should work with `start` >= `end`", (start) => {
    expect(slice(array, start, 2)).toEqual([]);
  });

  it("should work with a positive `end`", () => {
    expect(slice(array, 0, 1)).toEqual([1]);
  });

  it.each([3, 4, 2 ** 32, Infinity])(
    "should work with a `end` >= `length`",
    (end) => {
      expect(slice(array, 0, end)).toEqual(array);
    }
  );

  it("should work with a negative `end`", () => {
    expect(slice(array, 0, -1)).toEqual([1, 2]);
  });

  it.each([-3, -4, -Infinity])(
    "should work with a negative `end` <= negative `length`",
    (end) => {
      expect(slice(array, 0, end)).toEqual([]);
    }
  );
});

 

 

테스팅의 주요 개념들

  • 수동 vs. 자동화된 테스트
    • 수동 테스트
      • 매우 중요한 경로( SaaS 사이트의 회원 가입 / 결제 등 ) 에 대해 수동으로 진행
    • 자동화된 테스트
      • husky, git hook을 이용한 테스트 자동 실행
      • CI/CD 연동 테스트
      • 전체 앱의 기능, 동작들에 대해 다양한 실행 환경에서 테스트 진행
  • 테스팅 피라미드 - 유닛 vs 통합 vs e2e 테스트
    • 유닛 테스트
      • 하나의 기능, 역할을 담당하는 유닛을 고립된 상태에서 테스트
      • 유닛 테스트가 가장 기본적 “이 코드가 우리가 기대하는 대로 동작"한다는, 우리가 테스트를 작성함으로써 달성하고자 하는 목표는 기본적으로 유닛 테스트를 통해 달성
    • 통합 테스트
      • 여러 유닛들이 갑이 연결되어 동작할 때 기대대로 동작하는지 테스트
    • e2e 테스트
      • 중요한 동작들에 대해 실제와 동일한 환경/방식으로 테스트
    • 그리고 각각의 유닛이 기대대로 동작한다는 전제하에 유닛 사이의 상호작용을 통합테스트로 검증
  • 테스트 기반 개발(TDD, Test Driven Development)
    • 프로덕션 코드가 아니라 테스트 코드를 먼저 작성하고, 그 테스트 코드를 통과시킬 수 있는 프로덕션 코드를 작성해 나가는 방식
    • 장/단점이 있는 개발 방식
      • 테스트 환경을 준비/설정하는데 생각보다 큰 시간/비용이 발생한다는 점
      • 테스트 프레임워크는 실제 환경과 최대한 유사한 환경에서 코드를 실행하려고 하는데, 그러면서도 빠르고, 재현 가능하고, … 게 동작해야 함.
      • 이렇게 여러 요구 조건을 만족시키는 프레임워크는 구조적으로 복잡
  • 테스트 더블(Test double)
    • 실제 데이터, 객체, 모듈을 대신하는 객체
    • 그 역할이나 동작 방식에 따라 dummy, fake, mock, stub, spy 등이 있음
    • 테스트 프레임워크 , mock 프레임워크 별로 용어가 조금씩 다를 수 있음.
    • stub
      • 어떤 함수가 호출되면 어떤 정해진 값을 리턴하는 객체
      • mocking 프레임워크 필요.
    • mock
      • 어떤 함수가 어떤 파라미터랑 같이 호출될 것인지를 지정할 수 있고, 그렇지 않을 경우 테스트가 실패하게 할 수 있음.
      • mocking 프레임워크 필요.
    • fake
      • 실제 구현이 있지만 실제보다 단순화된 형태.
      • mocking 프레임워크 불필요.
  • 코드 커버리지
    • 전체 코드 라인 중 테스트 코드에 의해 실행되는 라인의 비율
  • 코드 커버리지 베스트 프랙티스
    • 이 지표가 테스트의 품질이나 프로덕션 코드의 품질을 담보하지 않음
    • 심지어 테스트에 의해 커버된 라인이 제대로 정확히 테스트 된 건지 알지 못함
    • 하지만 커버리지가 지나치게 낮은 것은 문제
    • 하지만 어느 만큼이 “지나치게 낮은” 것인지에 대한 합의는 없음

 

 

JS 테스트 프레임워크

  • Jest & Vitest
    • Jest
      • 페이스북에서 만든 압도적 1위 JS 테스트 프레임워크
    • mocha
      • 비동기 코드 테스팅에 특화된 JS 테스트 프레임워크
    • Vitest
      • vite 빌드툴을 위한 테스트 프레임워크
      • Jest와 호환됨. 최근 들어 사용하는 곳이 늘고 있음

 

좋은 테스트와 나쁜 테스트

  • 한 번에 한 가지만 테스트하기
    • “한 가지”는 하나의 “동작”을 의미 ( 하나의 함수에 여러 동작이 있는 경우도 있음 )
    • 하나의 테스트에서 검증하는 범위를 좁혀서 테스트하기
  • 테스트 이름 잘 짓기
    • 테스트 로직 , 코드를 보지 않고 테스트 함수 이름만 보고도 무엇을 테스트하는지 알 수 있어야 함
    • 테스트 이름에 테스트하는 시나리오, 기대하는 결과 값 포함하기
  • 구체적이고 명시적인 input 사용하기
    • 유닛 테스트는 어떤 input을 넣었을 때 기대하는 output이 나오는지 확인하는 것
    • input을 만들 때 최대한 구체적이고 명시적으로 만들기
    • production 코드와의 차이가 나는 부분
    • 복잡한 로직이 필요하거나 코드가 반복되는 경우 별도의 유틸로 빼내기
  • 원칙적으로 동작을 테스트하고 구현 디테일을 테스트하지 않기
    • 구현 디테일이 바뀌어도 동작이 바뀌지 않는 한 테스트는 변경되지 않도록 작성
    • 단, 예외적으로 구현 디테일을 테스트해야 되는 경우도 있음.
      • 주로 mock을 이용해 특정 함수, 코드가 호출되었는지를 확인하는 경우
      • ex) 데이터를 읽어오는데 DB가 아니라 캐시에서 읽어오는지를 검증
  • Mock 남용하지 않기
  • isolation vs realism
  • mock을 남용하면 테스트 대상이 정확히 동작하는 것인지 확신하기 어려워짐
  • 가독성과 유지보수성이 떨어짐
  • 네트워크 통신 등 속도가 너무 느리거나 동작 결과가 불안정하지 않으면 되도록 실제 구현 쓰기
  • 실제 구현을 쓰기 어렵다면 fake 구현 쓰기

 

 

Jest 테스트 프레임워크

  • Jest 소개
    • Meta에서 개발한 JS 테스팅 프레임워크
    • 여러 matcher들을 제공함
    • 필요한 경우 jest-extended 같은 matcher 라이브러리를 추가로 사용하는 것도 가능
      • expect(결과값).
      • toBe(), toEqual(), toHaveBeenCalled() …
    • 테스트 실행 환경 설정 : node vs. jsdom
  • 실행 환경 ( runtime environment )
    • 프로그램에게 제공되는( 접근 가능한 ) global object, library, API
      • 브라우저 = window, document, fetch, Web API(e.g. DOM API …),
      • Node = process.env
    • 소스 코드 파싱, 기계어로 번역
    • 메모리 관리, 쓰레드 관리
    • OS와 상호작용 등
  • Jest 설치하기
    • React
      • CRA: 자동으로 설정돼 있음.
    • Next.js
      • CNA: “--example with-jest”로 간편하게 설정
        • npx create-next-app@latest --example with-jest with-jest-app
      • 수동으로 설정하는 경우
        • jest 설정을 위한 패키지를 개발종속성으로 설치 ( 다음 중 하나 )
          • npm install -D jest jest-environment-jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom ts-node
          • yarn add -D jest jest-environment-jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom ts-node
          • pnpm install -D jest jest-environment-jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom ts-node
        • 기본 jest 구성파일을 생성
          • npm init jest@latest
          • yarn create jest@latest
          • pnpm create jest@latest
    • 기타
      • babel, webpack 등과 같이 사용하기
  • Jest matcher들
    • Matcher
      • expect() 함수의 리턴 객체에서 호출할 수 있는 다양한 비교 함수들
      • toBe(), toBeNull(), toBe{$...}
      • toEqual() = Object.is(deep equal)
      • toHaveBeenCalledTimes(number), toHaveBeenCalledWith(arg1, arg2, …)
      • toHaveProperty(keyPath, value?)
      • toBeCloseTo(number, numDigits?)
    • toBe() vs. toEqual() vs. toStrictEqual()
    • toStrictEqual()
      • 값이 undefined 인 속성들
      • 값이 undefined 인 배열
      • 등도 비교에 포함함
  • 수식어
    • .not
    • .resolves
    • .rejects
  • 여러 테스트를 구조화하기
    • describe()로 관련된 테스트끼리 묶기.
    • 모든 테스트 전/후에 실행해야 되는 코드: beforeEach() , afterEach()
    • 전체 테스트 전/후에 실행해야 되는 코드: beforeAll() , afterAll()
      • before, after 셋업은 describe() 안에서 호출도 가능
    • 기타
      • .only(), .skip()
  • 비동기 코드 테스트; 4가지 방법
    • Promises: 테스트 코드에서 promise 리턴하기
    • Async/Await:
    • callbacks(done() 함수):
    • .resolves, .rejects 수식어:
      • expect(fetchData()).resolves.toBe('peanut butter')
    • 비동기 코드 테스트할 때는 꼭 이 중 한가지 방법을 쓸 것 → 안 그러면 제대로 테스트되지 않고 통과될 수 있음
  • Mock을 사용해 테스트하기
    • jest.fn()으로 mock 콜백 만들기
    • jest.mock(‘모듈 이름’)으로 모듈 mocking 하기
    • jest.requireActual()으로 일부만 mocking 하고 나머지는 실제 코드 이용하기
    • mockFn.mockImplementation()으로 구현 대체하기
    • jest.mock(’모듈 이름’)
    • user module을 mock하기
      • mock 하려는 파일과 같은 위치에 mocks 디렉토리 만들고 mock 파일 위치
    • node module을 mock하기
      • jest.mock(‘모듈 이름’). e.g. ject.mock(‘axios’)
      • 또는 mocks 디렉토리에 mock 파일 놓기

 

 

 

 

+ 팀 미팅 후기

그렇게 됐다.

한 번 쯤은 팀장해보는 것도 좋다고 생각 했으니 이번 기회에 열심히 해봐야겠다.

반응형
저작자표시 비영리 변경금지 (새창열림)

'교육 > 코드잇 스프린트 : 단기심화 5기' 카테고리의 다른 글

[ 코드잇 스프린트 ] 교육기간 8일차 TIL  (1) 2024.11.18
[ 코드잇 스프린트 ] 교육기간 7일차 TIL  (1) 2024.11.16
[ 코드잇 스프린트 ] 교육기간 5일차 TIL  (1) 2024.11.14
[ 코드잇 스프린트 ] 교육기간 4일차 TIL  (3) 2024.11.13
[ 코드잇 스프린트 ] 교육기간 3일차 TIL  (0) 2024.11.12
'교육/코드잇 스프린트 : 단기심화 5기' 카테고리의 다른 글
  • [ 코드잇 스프린트 ] 교육기간 8일차 TIL
  • [ 코드잇 스프린트 ] 교육기간 7일차 TIL
  • [ 코드잇 스프린트 ] 교육기간 5일차 TIL
  • [ 코드잇 스프린트 ] 교육기간 4일차 TIL
heee1
heee1
FE 개발자를 희망하는 임희원 입니다.
  • heee1
    heee1.blog
    heee1
  • 전체
    오늘
    어제
    • 분류 전체보기 (69)
      • Front-end (1)
        • Javascript (4)
        • Typescript (1)
        • React (0)
        • Next.js (1)
        • Tool (1)
        • Git (1)
        • Prettier (0)
        • Test-Framework (1)
        • Vercel (1)
      • 교육 (28)
        • 항해99 : 웹개발 종합반 18기 (14)
        • 프로젝트 캠프 : React 2기 (5)
        • 코드잇 스프린트 : 단기심화 5기 (9)
      • Algorithm (29)
        • Javascript (24)
        • Python (5)
      • 코드 보관함 (1)
        • 배치스크립트 ( .bat ) (1)
  • 블로그 메뉴

    • 홈
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    Python
    Baekjoon
    node.js
    테스트 프레임워크
    speed insights
    항해99
    css animation
    JavaScript
    til
    티스토리챌린지
    모킹
    tailwindcss
    오블완
    백준
    자바스크립트
    jest
    next.js
    코드잇
    스프린트
    react-spring
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
heee1
[ 코드잇 스프린트 ] 교육기간 6일차 TIL
상단으로

티스토리툴바