메인 콘텐츠로 건너뛰기

React 단위 테스트: 실용적인 종단 간 안내서

React 단위 테스트를 설정부터 CI/CD까지 마스터하세요. 이 안내서에서는 Jest, RTL, 훅, 비동기 code, 모킹 및 강력한 크로스 플랫폼 앱을 위한 최선의 관행을 다룹니다.

Martin Donadieu

Martin Donadieu

콘텐츠 마케터

React 단위 테스트: 실용적인 종단 간 안내서

점심 전 작은 UI 변경을 푸시합니다. 그것은 무해해 보입니다. 버튼 레이블이 변경되며, 조건부 렌더링이 단순화되고, 도우미 훅이 새로운 branch를 선택합니다. pull request는 깨끗하고, 리뷰는 빠르고, 배포는 진행됩니다.

1시간 후, 지원팀이 로그인 기능이 하나의 플랫폼에서 작동하지 않는다는 보고를 받습니다. 웹은 정상입니다. 데스크톱 셸에는陈舊한 렌더 경로가 있습니다. 모바일 빌드는 비동기 상태 변경 후에 다르게 동작합니다. 아무도 그것을 잡지 못했습니다. 왜냐하면 code가 테스트를 가지고 있었지만, 올바른 테스트가 아니었고, 그 테스트를 신뢰할 수 있는 시스템도 없었습니다.

React 프로덕션 팀에서 단위 테스트하는 가장 큰 문제입니다. render()몇 개의 테스트를 작성하는 것은 쉽습니다. 그러나 리팩토링, 릴리스 트레인, 핫픽스, 크로스 플랫폼 패키징을 보호하는 테스트 스위트를 구축하는 것은 어렵습니다.

Modern unit testing React works when it behaves like a safety system. Fast feedback locally. Deterministic checks in CI. Clear boundaries around what belongs in a unit test and what doesn’t. That matters even more when the same React codebase ships through browsers, Capacitor containers, or Electron shells.

실패하는 이유는 테스트가 구현 세부 사항에 방향을 틀고, 비동기 동작이 가려지며, CI가 테스트를 체크박스로 대체하는 것입니다.

React에서 Unit Testing이 가장 안전한 안전망입니다.

Unit 테스트는 오류를 잡아내서 오류를 잡아내는 데 도움이 됩니다. React에서 일반적으로 오류는 컴포넌트가 렌더링되지만 사용자가 의존하는 동작이 변경된 경우입니다. 비활성화된 버튼이 클릭 가능해집니다. 로딩 상태가 절대 지워지지 않습니다. 리팩터링 후에 대체 메시지가 사라집니다. 이러한 오류는 code에서 작지만 프로덕션에서 비용이 많이 들 것입니다.

__CAPGO_KEEP_0__에서 React testing이 중요한 방식으로 바뀌었을 때 React Testing Library가 내부 구조보다는 동작을 테스트하는 mainstream 모델이 되었을 때, pushing teams toward tests that mirror user behavior rather than component props or state, as reflected in React Native’s testing guidance at React __CAPGO_KEEP_0__가 지속적으로 재배치되기 때문에. That shift matters because React code gets rearranged constantly. Hooks move. components split. Context gets introduced. A test tied to internal structure breaks during healthy refactors. A test tied to visible behavior usually survives.

단위 테스트가 보호해야 하는 것

좋은 React 단위 테스트는 하나의 작은 계약을 보호합니다:

  • 렌더링 된 출력: 사용자가 올바른 텍스트, 레이블, 상태 또는 기본값을 볼 수 있나요?
  • 상호 작용 동작: 클릭, 입력 또는 토글이 UI를 올바르게 변경하는지 확인하세요?
  • 경계 처리: 컴포넌트가 예상 입력, 누락된 데이터 또는 오류 경로를 받았을 때 올바르게 동작하는지 확인하세요?

약한 테스트는 잘못된 것을 보호합니다:

  • 컴포넌트 내부: 상태 형태, 사설 메서드, 구현 전용 속성
  • 프레임워크 메커니즘: 내부적으로 React가 hook을 정확히 어떻게 업데이트했는지 예상한대로 업데이트했는지 여부
  • 자녀 정보: 이곳에 존재하는 마크업은 중첩된 컴포넌트가 아닌 다른 컴포넌트가 소유하고 있습니다.

실용적인 규칙: 사용자가 보거나 할 수 있는 것을 변경하지 않고 컴포넌트를 리팩터링할 수 있다면, 테스트도 변경할 필요가 없습니다.

단위 테스트도 더 넓은 테스트 시스템에 속합니다. 그들은 앱이 끝까지 작동하는지 증명하려고 하지 않습니다. 그들은 빠른层면에서 회귀를 잡아내기 전에 브라우저 수준 테스트나 장치 수준 유효성 검사 패스를 필요로 하지 않습니다. 그들이 모든 합리적인 프로덕션 앱의 자동화된 테스트 스택의 첫 번째 수비대입니다. React 팀이 자주 배포하는 경우, 신뢰는 이 노동의 분할에서 오릅니다. 단위 테스트는 지역 회귀를 빠르게 잡아냅니다. 통합 테스트는 접합점을 검증합니다. 종단 간 테스트는 중요한 경로를 확인합니다. 단위层을 건너뛰면, 모든 더 느린 하위 시스템은 너무 많은 무게를 지고야 말 것입니다..

모던 React 테스트 환경을 설정하는 방법

fragile한 테스트 환경은 flaky한 테스트를 만들기 전에 단일 어설션을 작성하기 전에 발생합니다. 많은 개발자들은 Jest, jsdom, 또는 React를 비난하지만, 실제 문제는 로컬 머신과 CI에서 불일치하는 구성이 있습니다. 해결책은 환경을 재미없게 만드는 것입니다. 재미없다는 것은 여기서 좋은 것입니다.

청결한 작업 공간을 특징으로 하는 컴퓨터 모니터가 React 단위 테스트 __CAPGO_KEEP_0__를 표시하는 __CAPGO_KEEP_1__ 편집기를 보여줍니다.

A clean workspace featuring a computer monitor displaying React unit testing code in a code editor.

__CAPGO_KEEP_0__

현대 React 앱, 특히 Vite로 생성된 앱의 기본 설정에는 다음이 포함되어야 합니다:

  • 테스트 러너: Jest는 특히 이전 React 코드베이스와 기업 CI 스택에서 일반적입니다.
  • 브라우저와 같은 환경: jsdom 컴포넌트 테스트가 렌더링 DOM 출력을 허용합니다.
  • 테스트 라이브러리 유틸리티: @testing-library/react 그리고 @testing-library/jest-dom
  • 단일 설정 엔트리 포인트: 등록 매처와 글로벌 모의 객체를 등록하는 하나의 파일

React 테스트 지침이 강조하는 주요 워크플로는 간단합니다: jsdom-backed 환경에서 컴포넌트를 렌더링하고, getByText 또는 getByRole, 상호 작용을 트리거하고, DOM 변경을 확인하는 것입니다. React 테스트 문서. 그 워크플로가 신뢰할 수 있는지 유지하려면 모든 머신이 동일한 테스트 환경을 실행해야 합니다.

실용적인 Jest 설정은 일반적으로 다음과 같습니다.

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  moduleNameMapper: {
    '\\.(css|less|scss)$': 'identity-obj-proxy',
    '^@/(.*)$': '<rootDir>/src/$1',
  },
  transform: {
    '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
  },
};

팀이 SWC 대신 Babel을 사용한다면 괜찮습니다. 변환기라는 것이 중요한 것이 아니며, 일관성입니다. 하나의 경로를 선택하고 리포지토리에서 표준화하세요. 더 광범위한 자바스크립트 테스트 규칙에 대한 좋은 동반자 참고 자료를 원한다면 Capgo의 자바스크립트 단위 테스트 가이드 팀 내에서 유용한 전달 문서입니다.

설정 파일을 추가하여 스위트가 의존하는 파일

적절한 setupTests.js 반복적인 잡음:

import '@testing-library/jest-dom';

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(),
    removeListener: jest.fn(),
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

이 파일은 테스트 파일 20개 중에서 한 번에 환경 차이를 해결합니다. API에 의존하는 UI에 대한 모킹을 추가하십시오, 예를 들어 matchMedia, ResizeObserver, 또는 IntersectionObserver, 만약 컴포넌트 라이브러리가 그들을 기대한다면.

개발자는 globals를 임의로 수정하지 않아야 합니다. 그럴 경우 테스트가 일관되지 않고 오류가 발생합니다. 한 사람의 로컬 실행이 통과하는 이유는 그들이 파일에 수동으로 모킹을 추가했기 때문입니다. CI가 실패하는 이유는 설정이 공유되지 않았기 때문입니다.

CI와 로컬의 동작을 일치시키세요

로컬 명령어는 CI 명령어와 가능한 한 일치해야 합니다. 개발자가 watch 모드에서 허용적인 기본값으로 실행하지만 CI가 더 엄격한 설정으로 실행한다면, 병합 후에 놀라운 오류가 발생할 것입니다. 스크립트를 명확하게 유지하세요:

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:ci": "jest --runInBand --coverage"
  }
}

새 팀원들이 동일한 기준을 얻을 수 있도록 도와주는 짧은_walkthrough가 필요합니다:

기본값에 대한 discipline이 가장 큰 영향을 미치는 설정 선택입니다. alias를 config에 넣고 환경 모킹을 하나의 설정 파일에 넣으세요. 가능할 때 UI 테스트에 가벼운 환경을 사용하고 pure utility에 더 가벼운 환경을 사용하세요. 각 테스트가 필요한 custom behavior이 적을수록 시스템의 신뢰도가 높아집니다. jsdom 의미 있는 컴포넌트 테스트 쓰기

조직은 테스트를 작성하는 문제가 없습니다. 여섯 달 후에 여전히 중요하다는 것을 알리는 테스트를 작성하는 문제가 있습니다.

React 컴포넌트의 단위 테스트에 대한 표준 패턴은 여전히 올바른 패턴입니다:

컴포넌트를 렌더링하고 사용자 중심의 선택자로 UI를 쿼리하고 상호 작용을 트리거하고 결과적인 DOM 변경을 확인하세요 implementation details와 관련된 state나 props와 같은 것과 관련되지 않도록 테스트를 유지하는 것이 described된 React testing guide에서 설명한 것과 같습니다.__CAPGO_KEEP_0__ __CAPGO_KEEP_1__. 패턴을 적용하는 데는 조심스럽게 접근하는 것이 중요합니다.

사용자가 액세스하는 것처럼 액세스도어를 테스트하세요

기본적인 Accordion 컴포넌트를 선택하세요. 버튼과 제목이 렌더링됩니다. 패널 콘텐츠는 숨겨져 있습니다. 버튼을 클릭하면 콘텐츠가 표시되고 접근성 상태가 업데이트됩니다.

그것은 충분한 동작입니다. 몇 가지 유용한 테스트를 위한 것입니다:

  1. 초기 렌더링은 제목만 표시하지만 콘텐츠는 표시하지 않습니다.
  2. 트리거를 클릭하면 콘텐츠가 표시됩니다.
  3. 클릭하면 다시 축소됩니다.
  4. 접근성 속성은 표시 상태를 반영합니다.

마지막 점은 자주 생략됩니다. 사용자가 aria-expanded, aria-controls, 또는 역할 기반 구조를 사용하는 컴포넌트가 있다면, 그들을 확인하세요. 그것은 구현 세부 사항이 아닙니다. 그것은 사용자와의 계약입니다.

최선의 컴포넌트 테스트는 받고 싶지 않은 버그 리포트와 같습니다.

__CAPGO_KEEP_0__

React Testing Library는 여러 가지 쿼리 스타일을 제공하지만, 그들 사이에는 상호 교환할 수 있는 것은 아니다. 올바른 것을 선택하지 않으면 테스트가 노이즈나 오해를 불러일으킬 수 있다.

쿼리 타입 요소가 발견될 때 요소가 발견되지 않을 때 사용 사례 예시
getBy 요소가 즉시 반환된다. 에러가 즉시 발생한다. 버튼이나 헤딩이 이미 화면에 나타나야 한다는 것을 확인한다.
queryBy 요소가 즉시 반환된다. 반환 null 숨겨진 콘텐츠가 상호 작용하기 전에 존재하지 않아야 한다는 것을 확인한다.
findBy __CAPGO_KEEP_0__이 나타나면 해결됩니다. __CAPGO_KEEP_0__이 나타나지 않으면 거부합니다. fetch 또는 지연 업데이트 후에 로드된 콘텐츠가 나타나면 assert합니다.

간단한 정신 모델을 사용하세요.

  • 이미 존재해야 하는 것에 대해 사용하세요. getBy 아직 존재하지 않아야 하는 것에 대해 사용하세요.
  • UI가 나중에 변경될 때 사용하세요. queryBy 테스트가 시작되면
  • for 모든 것이라면, 일반적으로 작성자가 컴포넌트가 업데이트되는 시점을 모르는 것을 의미합니다. 그 불확실성이 나중에 불안정성을 의미합니다. findBy __CAPGO_KEEP_0__이 나타나면 assert합니다.

__CAPGO_KEEP_0__이 나타나지 않으면 거부합니다. findBy __CAPGO_KEEP_0__이 나타나면 assert합니다.

실용적인 접기 예시

이것은 대표적인 컴포넌트입니다:

function Accordion({ title, children }) {
  const [open, setOpen] = React.useState(false);

  return (
    <section>
      <button
        aria-expanded={open}
        aria-controls="accordion-panel"
        onClick={() => setOpen(prev => !prev)}
      >
        {title}
      </button>
      {open ? (
        <div id="accordion-panel">
          {children}
        </div>
      ) : null}
    </section>
  );
}

이러한 테스트의 형태는 유지보수에 도움이 아니라 신뢰를 얻기 위한 것입니다.

import { render, screen, fireEvent } from '@testing-library/react';

test('renders the accordion title and hides content initially', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  expect(screen.getByRole('button', { name: /shipping details/i })).toBeInTheDocument();
  expect(screen.queryByText(/delivery takes 3 days/i)).not.toBeInTheDocument();
});

test('reveals content when the trigger is clicked', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  fireEvent.click(screen.getByRole('button', { name: /shipping details/i }));

  expect(screen.getByText(/delivery takes 3 days/i)).toBeInTheDocument();
});

test('updates aria-expanded when opened', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  const button = screen.getByRole('button', { name: /shipping details/i });
  expect(button).toHaveAttribute('aria-expanded', 'false');

  fireEvent.click(button);

  expect(button).toHaveAttribute('aria-expanded', 'true');
});

내부 상태에 대한 주장, 호출된 것을 확인하는 것, 렌더링된 tree의 전체 스냅샷이 없는 것은 모두 중요합니다. setOpen 컴포넌트 테스트를 강화하는 몇 가지 습관이 있습니다:

역할 기반 쿼리 사용하기:

  • 버튼, 헤더, 대화상자, 경고, 입력은 일반적으로 역할로 찾습니다. 테스트를 좁게 유지하기:
  • 하나의 사용자에게 보이는 동작이 하나씩 테스트를 유지하면 실패가 읽기 쉽습니다. 테스트 이름을 결과에 맞추기:
  • “opened될 때 aria-expanded 업데이트”는 “정확히 작동한다”보다 훨씬 유용합니다. 이것은 Capgo의 강력한 컴포넌트 테스트 습관입니다.

DOM을 통해 테스트하기 어려운 컴포넌트가 있다면, 그게 디자인 문제를 드러내는 경우가 많습니다. 상태를 잘못된 위치에 숨기고 있거나, 의미 있는 마크업이 부족한 경우가 있습니다. 좋은 테스트는 팀을 더 나은 컴포넌트 방향으로 밀어내는 경우가 많습니다.

커스텀 훅스 및 애플리케이션 로직 테스트

리액트 앱은 컴포넌트 밖에서 중요한 동작을 숨기고 있습니다. 상태 전환은 훅스에, 유효성 검사 및 형식화는 도우미 함수에, 데이터 조작은 렌더링하기 전에 발생합니다. 만약에만 표시되는 컴포넌트만 테스트한다면, 여전히 프로덕션 동작을 깨는 code를 놓치게 됩니다.

훅은 리액트에 대한 인식 가능한 해쉬를 필요로 합니다

커스텀 훅스는 리액트가 실행될 수 있도록 리액트가 필요하므로, renderHook 그리고 상태 변경을 호출할 때 act().

작은 useToggle 훅은 좋은 예시입니다:

import { useState, useCallback } from 'react';

export function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);
  const toggle = useCallback(() => setValue(current => !current), []);
  return { value, toggle };
}

그의 테스트는 공개 계약에 집중해야 합니다:

import { renderHook, act } from '@testing-library/react';
import { useToggle } from './useToggle';

test('returns the initial value', () => {
  const { result } = renderHook(() => useToggle(true));
  expect(result.current.value).toBe(true);
});

test('toggles the value', () => {
  const { result } = renderHook(() => useToggle(false));

  act(() => {
    result.current.toggle();
  });

  expect(result.current.value).toBe(true);
});

그 테스트는 유용합니다. 훅 자체가 단위입니다. 리액트 내부를 테스트하는 것이 아니라, 훅의 외부 동작을 확인하는 것입니다.

상품 팀이 재사용 가능한 UI 또는 기능 원형을 빌드하는 경우, 이 패턴은 매우 중요합니다. 훅은 종종 앱, 디자인 시스템, 또는 내부 도구 간에 공유되는 인터페이스가 됩니다. 만약에 상업 목적으로 재사용 가능한 동작을 디자인하고 있다면, 훅에 대한 리소스 함께 프레임을 만들 수 있는 훅을 제품화된 빌딩 블록으로 보는 대신에 단순한 구현 세부 사항으로 보지 않도록 도와줍니다.

순수 논리는 테스트에서 순수하게 유지되어야 합니다.

모든 것이 필요하지는 않습니다. jsdomReact, 또는 Testing Library가 필요하지 않습니다. 함수가 순수하다면, Node 환경에서 단순한 Jest로 테스트하세요.

예시:

export function formatDisplayName(firstName: string, lastName: string) {
  return `${firstName.trim()} ${lastName.trim()}`.trim();
}

그 테스트는 매우 단순해야 합니다:

import { formatDisplayName } from './formatDisplayName';

test('joins and trims both names', () => {
  expect(formatDisplayName(' Ada ', ' Lovelace ')).toBe('Ada Lovelace');
});

test('handles a missing last name', () => {
  expect(formatDisplayName('Ada', '')).toBe('Ada');
});

이 승리에서 속도와 명확성이 있습니다. 함수가 렌더링된 tree가 필요하지 않다면, 그 함수에 렌더링된 tree를 제공하지 마십시오. React 관련 도구는 오버헤드를 추가합니다. 비즈니스 로직 테스트는 작고 빠르게 유지하고, 테스트하려는 함수와 가깝게 유지하세요.

실용적인 분리는 잘 작동합니다:

  • 훅: 필요한 경우에만 사용하세요. renderHook, act()유틸리티:
  • Utilities: 기본 Jest 사용 및 DOM 사용하지 않기.
  • 상태를 유지하는 교차 로직: 컴포넌트 테스트가 너무 많은 로직을 수행할 때 테스트 가능한 헬퍼로 끌어들이세요.

팀은 종종 컴포넌트 테스트에 로직을 포함시키는데, 그것은 스택의 아래쪽에 속하는 로직입니다. 로직을 끌어내면 두 가지 이점이 있습니다. 컴포넌트 테스트가 더 깨끗해지며, 로직 테스트가 더 빠르게 수행됩니다.

고급 기술을 마스터하는 방법: 모킹 및 비동기

대부분의 불신스러운 React 테스트 스위트는 두 가지 장소에서 깨집니다. 의존성 경계에서 깨집니다, 그리고 시간과 관련된 장소에서 깨집니다.

그것이 왜 비동기 테스트와 모킹이 진짜 테스트 스위트와 신뢰할 수 있는 테스트 스위트를 구분하는 선을 그리는 이유입니다. 하나의 분석은 46.5%의 테스트 불안정성을 환경 또는 자원과 관련된 문제로 인한 비동기 타이밍과 같은 문제로 인한 것으로 분석했습니다. 이 React 단위 테스트 분석에서. React 앱에서, 그것은 직접적으로 상태 전환, 지연 렌더링, 네트워크에 의한 UI, 그리고 테스트가 결정적으로 기다리지 않고 추측하는 테스트로 매핑됩니다.Mocking Dependencies versus Asynchronous Testing에 초점을 맞춘 Advanced React Testing Techniques 비교 차트.

__CAPGO_KEEP_0__

레이어를 모두 모킹하지 말라

위장 테스트를 가장 빠르게 작성하는 방법은 모킹한 컴포넌트 tree의 절반을 모킹하고, 나머지 모킹한 것들이 올바르게 작동하는지 확인하는 것이다.

계정 데이터를 가져오는 컴포넌트의 경우 네트워크 클라이언트 또는 API 모듈을 모킹하라. 모킹하지 말아야 하는 것은 hook, 자식 row 컴포넌트, 로딩 스피너, 세 가지 유틸리티 함수이다. 만약 테스트에서 truly 필요하다면 모킹할 수 있다.

이 규칙 세트를 사용하라:

  • 외부 서비스를 모킹하라: HTTP 클라이언트, 분석, 브라우저 전용 API, 네이티브 브리지를 모킹하라
  • 불안정한 플랫폼 API를 모킹하라: matchMedia타이머, Electron preload 인터페이스, Capacitor 플러그인(JavaScript DOM에서 사용할 수 없는 경우)
  • 기본적으로 자신의 내부를 모킹하지 말라: 커스텀 훅, 간단한 자식, 로컬 유틸리티

모든 어려운 부분을 가짜로 대체하여 테스트가 통과하는 경우, 이는 릴리스에 대한 신뢰를 얻은 것이 아니다.

런너 API에 대한 예시와 패턴을 원하는 팀에게 Capgo를 제공한다. Jest 카테고리 __CAPGO_KEEP_0__

시간이 모호할 때 비동기 테스트가 실패합니다.

비동기 테스트가 실패하는 경우 일반적으로 세 가지 오류 중 하나 때문입니다.

  1. 테스트가 너무 일찍断言합니다.
  2. 테스트가 임의의 타이머로 대기합니다.
  3. 컴포넌트가 여러 번 업데이트되지만 테스트는 하나의 전환만 모델링합니다.

안정적인 비동기 테스트는 보통 이 형태를 가집니다.

test('shows user details after data loads', async () => {
  render(<UserProfile userId="42" />);
  expect(screen.getByText(/loading/i)).toBeInTheDocument();

  expect(await screen.findByText(/account owner/i)).toBeInTheDocument();
});

특정 조건을 기다릴 때는

await waitFor(() => {
  expect(screen.getByRole('alert')).toBeInTheDocument();
});

원하는 요소의 나타남을 이벤트로 관심을 기울이고 싶을 때 사용하세요. findBy 조건이 더 넓거나 상태를 단일 쿼리로 표현할 수 없을 때 사용하세요. waitFor 사용하지 마세요. setTimeout 테스트에서만 사용하는 경우 timer 동작을 명시적으로 테스트하고 가짜 타이머를 사용하는 경우를 제외하고.

React의 테스트 생태계는 업데이트에 대한 의미를 존중하는 것을 기대합니다. act() 업데이트가 플러시되는 시점을 생각해야 합니다.

어떤 mocking 도구를 사용해야 할지 알 수 있습니다.

다른 mocking 도구는 다른 문제를 해결합니다.

Tool Best use Common mistake
jest.fn() 독립적인 가짜 콜백 또는 주입 함수 단순한 콜백이 충분한 경우에 모듈 전체를 대체하는 것을 사용하는 경우
jest.spyOn() 실제 객체 또는 모듈의 한 메서드를 관찰하거나 오버라이드하는 경우 원래 구현을 복원하지 않는 것을 잊는 경우
jest.mock() 모듈 의존성을 임포트 경계에서 교체하십시오. 대량 모듈을 기본적으로 모킹하고 의미 있는 동작을 잃습니다.

예시 도움말:

  • 찾아보기 jest.fn() 컴포넌트가 onSubmit 속성을 받을 때.
  • 사용 jest.spyOn() __CAPGO_KEEP_0__을 내보내거나 저장하는 방법을 확인할 때. console.error, a storage method, or one exported API call.
  • 모듈을 임포트할 때 I/O, 네이티브 __CAPGO_KEEP_0__, 또는 단위 경계 외부의 동작을 피할 때. jest.mock() when importing a module would otherwise hit I/O, native code, or behavior outside the unit boundary.

사용

테스트 품질과 전략을 개선하는 방법

많은 팀이 여전히 보장과 같은 것으로 보장과 신뢰를 구별하지 못하고 있습니다.

보장은 목표가 아니며, 목표를 달성한 후에도 중요한 회귀 테스트를 놓치게 됩니다.

품질 테스트의 이점과 고량 테스트 스위트의 유지 보수 오버헤드 비교를 보여주는 그래픽.

보장은 지도이며 목표가 아님

보장 보고서는 다음 질문에 답할 때 유용합니다: 아직 보호되지 않은 중요한 경로가 무엇인가요?

개발자들을 보장 보고서로 인해 불필요한 테스트를 하도록 유도하지 말아야 합니다. 보장은 발견 도구로 사용해야 합니다. 인증 상태, 결제 액션, 기능 플래그, 업데이트 알림이 테스트되지 않은 경우 신호입니다. 그러나 사용자 인터페이스 아이콘 컴포넌트가 테스트되지 않은 경우 일반적으로 그렇지 않습니다.

건강한 검토 질문은 간단합니다: 이 테스트는 릴리스 위험을 줄이는가?

  • Yes: 사용자에게 보이는 중요한 경로의 동작을 검증합니다.
  • Maybe: 비즈니스 로직을 보호합니다. 이 로직은 리팩토링 중에 쉽게 깨질 수 있습니다.
  • No: 구현 세부 사항을 명시하거나 다른 테스트의 값을 복제하는 경우입니다.

React에서 무엇을 테스트하지 않는 것이 좋습니다

React에 대한 많은 가이드가 여전히 생략에 충분한 시간을 보내지 않습니다. 그 틈새는 중요합니다. 왜냐하면 브라우저 스택의 React에서 무엇을 테스트하지 않는 것이 좋습니다 가이드에 설명된 것처럼 과도한 모킹과 구현 세부 사항 테스트는 사용자 경험을 깨트리는 동안 테스트가 통과하는 brittle한 테스트 스위트를 만듭니다. 다음 패턴을 생략하거나 엄격하게 제한하세요:.

내부 상태 확인:

  • 직접 테스트하지 마세요. panel이 열렸는지 여부를 테스트할 수 있습니다. 프레임워크 동작: isOpen React가 효과를 호출했는지 테스트하지 마세요. 효과가 변경한 결과를 테스트하세요.
  • 세 번째-party 라이브러리 내부: 테스트하지 마세요.
  • 직접 테스트하지 마세요. 테스트를 위한 통합을 날짜 선택기나 라우터와 함께 테스트하세요. 라이브러리의 렌더링 로직이 아닌.
  • 잘못된 단위: 모든 자식과 헬퍼를 모킹했다면 더 이상 의미 있는 동작을 테스트하지 못할 수 있습니다.

잘못된 테스트는 리팩토링을 막고 여전히 프로덕션 버그를 잡지 못하는 테스트보다 나쁩니다.

유용한 가이드라인은 경계 소유입니다. code이 소유한 것을 테스트하세요. React, 브라우저, 또는 성숙한 라이브러리가 이미 소유한 것을 테스트하지 마세요. 단, 통합层에서 계약을 변경한다면.

샷샷이 도움이 되는 곳과 도움이 되지 않는 곳

샷샷은 쓸모가 없습니다. 쉽게 잘못 사용하기 때문입니다.

샷샷은 구조적 diff가 의미 있는 컴포넌트에만 사용하세요. 반응형 또는 동적이 아닌 컴포넌트에 사용하세요. 반응형 또는 동적 컴포넌트에 사용하면 노이즈가 됩니다. 개발자는 읽지 않고 자동으로 업데이트 합니다.

보다 좋은 대안이 있습니다:

  • 조건부 렌더링을 테스트할 때, 키 텍스트의 존재 또는 비존재를 확인하세요.
  • 시각적 상태 변경을 테스트할 때, 중요한 역할, 레이블, 또는 속성을 확인하세요.
  • 에러와 폴백을 테스트할 때, 실제 메시지 또는 알림 영역을 확인하세요.

If your team needs a broader quality process beyond unit tests, a solid companion is an 애플리케이션 품질 보증 워크플로우 tests, release checks, and rollback planning as one system. That’s the mindset shift that improves test quality fastest. Stop asking how many tests you have. Start asking which failures could still reach users.

Cross-Platform CI/CD Pipeline에 테스트 통합

A test suite that only runs on a developer laptop is a suggestion, not a control.

CI/CD Pipeline에서 테스트가 작동하는 시점은

Tests run manually. Coverage reports are optional. Packaging and release jobs start before test jobs have finished. That’s how small UI regressions slip into bigger release failures.

CI/CD Pipeline에 테스트 통합하는 5단계

A pull request should trigger the same gate every time

  • React의 단위 테스트를 위한 CI의 몇 가지 필수 요소
  • Run on every pull request
  • Install dependencies from lockfile (lock파일에서 의존성을 설치합니다.)
  • 테스트 실패 시 빠르게 실패
  • 테스트가 통과한 후에만 빌드_artifacts_를 배포

이것이 앱 팀을 위한연속적인 배포_continuous deployment_ 관행의 핵심입니다.

A simple GitHub Actions workflow is enough for many teams:

name: test

on:
  pull_request:
  push:
    branches:
      - main

jobs:
  react-tests:
    runs-on: ubuntu-latest

    steps:
      - name: Check out code
        uses: actions/checkout@v4

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Run unit tests
        run: npm run test:ci

많은 팀에게는 간단한 __CAPGO_KEEP_0__ Actions 워크플로우만 필요합니다.

Why this matters more for Capacitor and Electron

이것이 code와 Electron에 더 중요하다는 이유는

브라우저만 지원하는 앱과 달리, 크로스 플랫폼 React 앱은 더 많은 배포 위험을 가지고 있습니다. 왜냐하면 동일한 UI __CAPGO_KEEP_0__가 종종 다른 컨테이너에 다른 런타임 가정과 함께 배포되기 때문입니다.

  • Capacitor apps: code 앱:
  • Electron 앱: 렌더러 컴포넌트는 브라우저 테스트에서 존재하지 않는 preload API, 윈도우 메시징, 또는 데스크톱 전용 상태에 의존할 수 있습니다. 이 경우에는 의도적으로 모킹해야 합니다.
  • 공유 릴리스 트레인: 하나의 잘못된 배포본이 여러 대상에 영향을 미칠 수 있습니다. 만약 배포 프로세스가 엄격하게 배포를 제어하지 않는다면.

그것은 단위 테스트가 패키징 작업 전에 실행되어야 하며, 패키징 작업이 배포 작업 전에 실행되어야 한다는 것을 의미합니다. 각 단계는 위험을 줄입니다. 단위 테스트는 지역적인 회귀를 빠르게 잡을 수 있습니다. 플랫폼 패키징은 환경 가정의 검증을 수행합니다. 마지막 릴리스에 대한 신뢰를 처리하는 데에는 수동 승인 또는 스테이지드 롤아웃이 필요합니다.

실용적인 GitHub Actions 워크플로

더 성숙한 PIPELINE은 책임을 나누어야 합니다:

  1. 테스트 작업: 빠른 단위 및 훅 테스트
  2. 빌드 작업: 테스트가 통과한 후에만 프로덕션 빌드를 생성합니다.
  3. 패키징 작업: Capacitor 동기화, Electron 패키징, 또는 아티팩트 번들링
  4. 릴리즈 잡: 만약에 승인된 branch 또는 tag에서만 릴리즈를 Publish 하도록 하세요.

팀이 Capacitor 또는 Electron 앱에 실시간 업데이트를 배포하는 경우, 릴리즈 툴링이 중요합니다. 그 workflow에서 하나의 옵션은 Capgo입니다. 이 툴은 CapacitorJS 및 Electron 앱에 서명된 웹 번들을 릴리즈하고 롤백 지원 및 채널 기반 롤아웃 제어를 제공합니다. 실제로, 그 의미는 React 테스트 잡이 웹 번들을 스테이징 또는 프로덕션 배포로 승격하기 전에 첫 번째 강제 게이트 역할을 할 수 있게 됩니다.

작동 규칙은 간단합니다. 릴리즈 인프라가 약한 테스트를 보상하지 않도록 하세요. 릴리즈 인프라를 사용하기 전에 신뢰할 수 있는 테스트가 이미 잘못된 변경 사항을 필터링出了 후에 사용하세요.

신뢰할 수 있는 테스트 시스템은 팀의 행동을 바꿉니다. 개발자들은 더 많은 용기 있게 머지합니다. 리뷰어들은 에지 케이스에 집중하기 보다는 기본적인 것을 수동으로 다시 실행하지 않습니다. 릴리즈 매니저들은 배포를 gamble로 다루지 않습니다. 그게 신뢰할 수 있는 React 단위 테스트를 하는 결과입니다.


팀이 React를 Capacitor 또는 Electron을 통해 배포한다면, 릴리즈 안전은 단순히 로컬 테스트가 green인 경우에만 의존하지 않습니다. Capgo 팀에게는 서명된 웹 업데이트를 릴리즈하고, 롤아웃 채널을 목표로 하며, 나쁜 번들을 롤백할 수 있는 제어된 방법을 제공합니다. 스토어 리뷰를 기다리지 않고, CI pipeline가 이미 배포 전에 단위 테스트를 통과해야 한다는 요구를 이미 충족하고 있는 경우에 자연스럽게 이 툴을 사용할 수 있습니다.

Capacitor 앱에 대한 실시간 업데이트

웹层 버그가 활성화된 상태에서 Capgo를 통해 픽스를 배포하는 것이 앱 스토어 승인 대기일보다 더 빠르다. 사용자는 배경에서 업데이트를 받으면서 네이티브 변경은 일반적인 검토 경로에 남아있다.

시작하기

블로그에서 최신 뉴스

Capgo는 전문적인 모바일 앱을 만들기 위해 필요한 최고의洞察력을 제공한다.