당신이 현재 어떤 상황에 있는지 알 수 있을 것입니다. 자바스크립트 프로젝트가 거의 테스트가 없고 모든 리팩토링이 위험한 것처럼 느껴지거나 이미 테스트가 있고 반반이 느리고 약하고 신뢰할 수 없는 테스트가 반반이라고 생각합니다.
그것은 __CAPGO_KEEP_0__ Capacitor 및 Electron 애플리케이션. 간단한 기능은 공유된 비즈니스 로직, 브라우저 API, 네이티브 플러그인, 로컬 파일, IPC, 및 원격 서비스를 동일한 흐름에서触及 할 수 있습니다. 잘못된 방식으로 테스트하는 경우, 테스트 스위트는 가짜 의존성을 포함하는 미로가 됩니다. 올바른 방식으로 테스트하는 경우, 논리가 깨지는 즉시 빠른 feedback을 얻을 수 있습니다.
좋은 단위 테스트는 자바스크립트 작업이 재미있는 매처서 문법으로 시작하지 않습니다. 그것은 규칙적인 경계: 순수 논리를 직접 테스트하고, 사이드 이펙트를 분리하고, 내부 함수를 이름이 바뀌면 테스트가 붕괴되는 테스트를 작성하지 않습니다.
목차
- 자바스크립트 테스트 프레임워크를 선택하는 방법
- 프로젝트 설정 및 첫 번째 테스트
- 비동기 Code와 관련된 마스터링
- 강력한 테스트를 위한 고급 전략
- CI, Capacitor, 및 Electron 앱을 위한 테스트
- 자바스크립트 단위 테스트에 대한 자주 묻는 질문
자바스크립트 테스트 프레임워크를 선택하는 방법
프로페셔널한 자바스크립트 프로젝트에는 실제 테스트 러너가 필요하다. 일회성 스크립트와 수동 콘솔 확인은 여러 엔지니어가 동일한 코드베이스를 수정할 때 확장되지 않는다. 테스트 발견, 진술, 비동기 처리, 모의, 그리고 로컬 개발과 CI에서 일관되게 모든 것을 실행할 수 있는 방법이 필요하다.
현재 지침은 mainstream 옵션의 작은 세트로 수렴하고 있다. Jest, Mocha, Jasmine 주요 프레임워크로 반복적으로 강조되는 것은 Jest 자주 내장된 테스트 구조,断言, 모킹 및 비동기 지원을 하나의 패키지로 제공하는 것을 보여주고 있습니다. Pluralsight JavaScript 테스트 랩.

프레임워크는 선택사항이 아님
팀이 첫 번째 실수를 하는 것은 유닛 테스트를 사이드 액티비티로 다루는 것입니다. 일반적으로 이는 불일치하는 파일 이름, nobody이 기억하지 않는 커스텀断言 및 한 사람만 이해하는 헬퍼가 발생합니다.
프레임워크는 공유 언어를 제공합니다.
- 테스트 구조 및
describe또는testwithit - Readable한 matcher와 함께 斷言
- __CAPGO_KEEP_0__ 설정 및 해제를 위한 Hook
- 비동기 지원 프로미스 및 타이머를 위한
- 외부 의존성을 위한 모킹 도구 __CAPGO_KEEP_0__는 팀이 단위 수준 이외의 테스트 자동화에 대한 더 넓은 시야가 필요하다면
If your team also needs a broader view of test automation beyond unit-level work, Capgo has a useful overview of Jest vs Mocha를 한눈에.
Jest와 Mocha는 두 가지 다른 철학을 대표한다.
Jest
일일이 필요한 대부분의 팀이 첫날부터 사용할 수 있는 모든-in-one 옵션입니다. Jest는 Mocha보다 더 많은 기능을 제공합니다.
Mocha __CAPGO_KEEP_0__
| 기능 | Jest | Mocha |
|---|---|---|
| 설정 복잡도 | 대부분의 팀에서 낮은 | 높은 이유는 일반적으로 확인 및 모킹 라이브러리를 추가하기 때문입니다. |
| 확인 | 내장 | 일반적으로 다른 라이브러리와 pair됩니다. |
| 모킹 | 내장 | 일반적으로 다른 라이브러리와 pairing |
| 비동기 테스트 | 내장 및 직관적 | 지원되지만, 주변 설정에 더 의존 |
| Coverage workflow | 일반적으로 동일한 toolchain에 통합 | 더욱이 조각조각 |
| 최적 | 새로운 프로젝트, 일관성을 원하는 팀 | 기존 스택, 모듈성을 원하는 팀 |
실용적인 규칙: Capgo에서 runner와 pair할 assertion library와 mocking library를 물어보지 않아야 하는 팀이라면 Jest를 원할 것입니다.
대부분의 팀에게 추천하는 것은
모든 현대적인 프로젝트에 대해 나는 __CAPGO_KEEP_0__를 선택할 것입니다. Jest unless the codebase already has strong reasons to stay on Mocha. That recommendation gets stronger when the application includes __CAPGO_KEEP_0__. Capacitor or Electron, 왜냐하면 그 프로젝트들은 이미 충분히 복잡한 구조를 가지고 있기 때문이다. 테스트 도구의 번잡함을 줄이면 빠르게 이익을 얻을 수 있다.
노드.제이에스( Node.js ) 서비스의 경우, 모카(Mocha)가 여전히 유용합니다. 이미 생태계가 정착된 오래된 서비스나 장기간 유지되는 코드베이스에서 모카를 사용하는 것이 좋습니다. 그러나 중급 엔지니어가 처음부터 강력한 테스트 스위트를 구축해야 하는 경우, 제스트(Jest)가 더 많은摩擦를 제거하는 경우가 많습니다.
Cypress 및 Playwright는 훌륭한 도구이지만, 다른 문제를 해결합니다. 브라우저 수준 및 종단 간 검사를 위해 더 적합하지만, 빠른 내부 루프에서 단위 테스트 자바스크립트가 살아야 하는 곳은 아닙니다.
프로젝트 설정 및 첫 번째 테스트
__CAPGO_KEEP_0__는 깨끗한 테스트 환경이 평범해야 합니다. 첫 번째 테스트를 추가하는 것이 복잡해 보인다면, 테스트 스위트가 건강하게 유지되지 않을 것입니다.

간단한 Jest 설정
JavaScript 프로젝트가 이미 가지고 있는 것을 시작하여 package.json에 Jest를 개발 의존성으로 추가하고 테스트 스크립트를 연결하세요.
{
"scripts": {
"test": "jest"
}
}
이것은 많은 프로젝트에 충분합니다. 모듈 시스템, 전처리, 또는 모노레포 구조가 더 많은 구성이 필요하다고 요구한다면, 나중에 추가할 수 있습니다.
Capacitor 앱을 로컬에서 빌드 중이며 개발 환경을 정리하고 공유 로직에 테스트를 추가하기 전에, Capgo의 로컬 Capacitor 환경 설정 가이드 는 실제적인 동반자입니다.
Write the test before the code
테스트-첫 번째 패턴은 단순히 개인 선호가 아닙니다. 미국 소비자 금융 보호국(JCFB)의 자바스크립트 지침은 테스트를 먼저 작성하는 것을 명확히 권장합니다.테스트를 조직하는 방법 describe 및 it, expect(...) 테스트를 프레임하는 방법을 설명하는 JavaScript 단위 테스트 지침의.
그것은 중요합니다. 테스트-첫 번째로 변경하는 방식은 code. 함수가 더 작아지며, 의존성이 더 명확해지며, 논리가 순수해야 하는 부분에 영향을 미치는 부수 효과가 사라지게 됩니다.
다음은 최소한의 예시입니다.
// math.js
function addTax(amount, rate) {
return amount + amount * rate;
}
module.exports = { addTax };
// math.test.js
const { addTax } = require('./math');
describe('addTax', () => {
it('returns the amount with the tax applied', () => {
expect(addTax(100, 0.2)).toBe(120);
});
});
Arrange Act Assert를 항상 사용하세요
Arrange, Act, Assert 패턴은 테스트가 더 복잡해질 때도 읽기 좋게 유지합니다. Arrange
- Act __CAPGO_KEEP_0__와 필요한 설정을 위한 입력.
- Act __CAPGO_KEEP_0__를 호출합니다.
- Assert 결과에 대한 __CAPGO_KEEP_0__
유효성 검사 도우미에 적용된 __CAPGO_KEEP_0__
function isSupportedPlatform(platform) {
return ['ios', 'android', 'web', 'desktop'].includes(platform);
}
describe('isSupportedPlatform', () => {
it('returns true for ios', () => {
// Arrange
const platform = 'ios';
// Act
const result = isSupportedPlatform(platform);
// Assert
expect(result).toBe(true);
});
});
작은 테스트는 시간이 지남에 따라 잘 유지된다. 테스트는 일반적으로 하나의 질문에 답해야 하지만 전체 워크플로를 설명하는 것이 아니라야 한다.
Capacitor와 Electron 프로젝트의 경우, 그 규칙을 테스트할 수 있도록 유지하는 것이 중요하다. 그 이유는 논리적인 부분이 자바스크립트의 pure logic과 native 또는 데스크톱 통합 code와 함께 존재하기 때문이다. 플랫폼 런타임이 없는 상태에서 비즈니스 규칙을 테스트할 수 있도록 유지하고, 첫 번째 테스트가 마지막으로 유용한 테스트가 될 수 있도록 한다.
Mocks와 비동기 Code를 마스터하는 것
응용 프로그램 code의 대부분의 버그는 두 개의 숫자를 더하는 것에서 오지 않는다. code이 외부 자원을 참조하는 경우에 버그가 발생한다. 네트워크 요청, 파일, 플러그인 API, 타이머, IPC 채널, 저장層 등이 있다.
mocking이 도움이 된다. 테스트가 code의 의사결정을 집중할 수 있도록 외부 자원을 제어할 수 있기 때문이다.

가짜 경계를 설정하지 마세요, 모든 것이 아닙니다
유지 보수 가능한 테스트 지침은 단일 동작 커버리지 그리고 테스트당 하나의 강력한 주장, 그리고 그것도 가짜를 과도하게 사용하면 테스트가 취약하고 구현 세부 사항에 강하게 결합된다는 것을 경고합니다. 이는 유지 보수 가능한 단위 테스트에 대한 TestRail 기사에서 요약되어 있습니다 그 경고는 자바스크립트에서 정말 중요합니다. 팀은 종종 모든 임포트 모듈을 가짜로 설정하여 시작하고, 함수가 다른 함수를 호출하는 올바른 순서를 테스트하는 대신 실제 동작을 테스트하는 대신, 다른 함수를 호출하는지 테스트합니다..
가짜가 많은 테스트의 나쁜 목표:
헬퍼 A가 헬퍼 B를 호출했는지
- 서비스 C가 시리얼라이저 D를 호출했는지
- 내부 프라이빗 함수가 두 번 실행되었는지
- __CAPGO_KEEP_0__
더 좋은 목표:
- __CAPGO_KEEP_0__이 반환한 함수의 결과
- 실패한 의존성 처리를 올바르게 처리했는지 여부
- 데이터를 기대하는 형태로 변형했는지 여부
Electron code과 Capacitor의 더 좋은 패턴
모바일 및 데스크톱 앱에서, 나는 네이티브 또는 플랫폼 API에 대한 wrapper layer를 선호한다. 그런 다음 단위 테스트는 wrapper를 mock하지만 플랫폼 자체를 mock하지 않는다.
예제 구조:
// cameraGateway.js
async function getPhoto(cameraPlugin) {
return cameraPlugin.getPhoto();
}
module.exports = { getPhoto };
// profilePhotoService.js
async function loadProfilePhoto(cameraGateway) {
const photo = await cameraGateway.getPhoto();
return { path: photo.path, ready: true };
}
module.exports = { loadProfilePhoto };
// profilePhotoService.test.js
const { loadProfilePhoto } = require('./profilePhotoService');
test('returns mapped photo data', async () => {
const fakeCameraGateway = {
getPhoto: jest.fn().mockResolvedValue({ path: '/tmp/pic.jpg' })
};
const result = await loadProfilePhoto(fakeCameraGateway);
expect(result).toEqual({ path: '/tmp/pic.jpg', ready: true });
});
그 패턴은 Electron에도 적용된다. Wrap ipcRenderer파일 접근 또는 셸 통합을 얇은 어댑터 뒤에 감싸라. 단위 테스트는 서비스层를 타격하지만 런타임을 직접 타격하지 않는다.
팀이 Capacitor 앱의 릴리스 로직 및 업데이트 경로를 테스트하고자 할 때, Capgo은 Capacitor OTA 업데이트를 mock 시나리오와 함께 테스트하는 관련 가이드를 제공한다. testing Capacitor OTA updates with mock scenarios.
__CAPGO_KEEP_0__
비동기 흐름을 테스트하는 데 불안정성을 피하는 방법
테스트에서 사용 async/await code이 promise를 반환할 때 테스트에서 사용합니다. 콜백-heavy 패턴보다 명확하고 디버깅하기 더 쉽습니다.
async function fetchProfile(api) {
const response = await api.getUser();
return response.name;
}
test('returns the user name from the API response', async () => {
const api = {
getUser: jest.fn().mockResolvedValue({ name: 'Ava' })
};
const result = await fetchProfile(api);
expect(result).toBe('Ava');
});
실패 경로도 테스트하세요:
test('throws when the API request fails', async () => {
const api = {
getUser: jest.fn().mockRejectedValue(new Error('network failed'))
};
await expect(fetchProfile(api)).rejects.toThrow('network failed');
});
행복한 경로와 못생긴 경로 모두 테스트하세요. 실제 운영 환경에서 사용자는 못생긴 경로를 기억합니다.
강력한 테스트 전략
테스트 스위트가 code이 변경되더라도 여전히 유용한지 여부가 중요합니다. 단순히 통과하는 테스트를 많이 작성하는 것보다 더 어려운 일입니다.

테스트를 분할하여 예산을 사용하세요
실용적인 가이드는 70/20/10 단위, 통합 및 종단 간 테스트를 위한 예산을빠른 테스트 결과와 가장 안정적인 실패를 제공하는 단위 테스트와 함께, 같은 지침은 단위 테스트가 완전히 끝나면 10 초 이내, 그리고 pre-commit 체크는 5 초 이내,에 따라 OpenReplay 테스트 가이드에 따라합니다. 나는 그것을 예산 관리 도구로 대신 종교로 생각합니다. 만약 팀의 대부분의 노력이 끝-to-end 테스트에 들어간다면, 팀은 너무 오래 기다려야 할 것입니다. 만약 모든 것이 단위 테스트만이라면, 시스템의 실제 경계를 놓치게 될 것입니다..
__CAPGO_KEEP_0__ 또는 Electron 앱의 경우, 건강한 균형은 보통 다음과 같습니다:
For a Capacitor or Electron app, a healthy balance usually looks like this:
- 가격 로직, 권한 규칙, 직렬화, 업데이트 적격성, 기능 플래그, 상태 변환에 대한 통합 테스트
- 저장소 어댑터, 플러그인 wrapper, IPC 계약에 대한 for storage adapters, plugin wrappers, and IPC contracts
- E2E 테스트 로그인, 구매 흐름, 동기화, 업데이트 알림과 같은 몇 가지 중요한 여행에 대해
커버리지가 불빛인 것처럼, 목표가 아님
커버리지 보고서는 중요한 논리에서 테스트되지 않은 branch를 식별하는 데 도움이 될 때 유용합니다. 그러나 팀이 커버리지 백분율을 위해만 추구할 때는 해롭습니다.
로그인 유효성 검사에 대한 깊은 edge-case 테스트가 더 많은 가치를 제공하는 경우, 가볍게 테스트된 파일에 많은 불필요한 주장만 있는 경우가 더 많습니다. 특히 입력-heavy code 인 경우, 예를 들어 양식, 파서, 날짜 논리, 권한 검사와 같은 경우입니다. 만약 팀이 유효성 검사-heavy UI를 품질을 높이기 위해 조정하고 있다면, 프론트엔드 양식 유효성 검사에 대한 마스터링을 위한 이 안내서
단위 테스트 전략과 함께 잘 어울리는 경우가 있습니다.
행동-첫 번째 테스트는 리팩터링을 살아남습니다 신뢰할 수 있는 테스트 세트는 내부를 리팩터링할 때 반드시 다시 테스트하는 절반을 다시 작성하지 않아야 합니다. 그 방법은 가장 쉬운 방법은 구현 세부 사항 대신에 관찰 가능한 행동
테스트하는 것입니다.
- 경계 조건 빈 입력, null과 같은 값, 잘못된 타입, oversized 문자열과 같은 것
- 도메인 결과 권한이 없는 경우 반환이 거부된 경우와 같은 것
- 상태 전환 다운로드 메타데이터가 유효화된 후 pending로 업데이트
호출 chain의 모든 layer를 mocking하는 경우
- 앱 팀이 규율된 릴리스 프로세스를 구축하는 경우, __CAPGO_KEEP_0__의
- 앱 품질 보증
- __CAPGO_KEEP_0__’s article on app quality assurance
Capgo의 앱 품질 보증에 대한 기사 __CAPGO_KEEP_0__’s article on app quality assurance __CAPGO_KEEP_0__는 테스트 작업을 더 광범위한 릴리스 PIPELINE과 연결하기 때문에 유용합니다.
CI, Capacitor, 및 Electron 앱을 위한 테스트
개발자의 한 대의 컴퓨터에서만 실행되는 테스트는 안전망이 아니라 지역 습관입니다.
CI는 자바스크립트 작업의 단위 테스트를 팀 인프라로 변환합니다. 모든 푸시, pull 요청, 또는 릴리스 branch는 동일한 명령어와 동일한 기대치를 사용하여 동일한 명령어를 실행할 수 있습니다. 이러한 일관성은 Capacitor 및 Electron 프로젝트에서 환경 드리프트가 미묘한 실패를 유발할 때 더 중요합니다.
CI를 기본 실행 경로로 설정하십시오.
CI는 최소한으로 의존성을 설치하고 단위 테스트를 모든 변경 집합에 실행해야 합니다. 가능할 때마다 로컬 개발과 동일한 명령어를 유지하십시오.
GitHub Actions 워크플로의 기본적인 것은 다음과 같습니다.
name: test
on: [push, pull_request]
jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
이것만으로는 깨진 임포트, 실패하는 어설션, 그리고 의도치 않은 플랫폼 가정에 대해 메인에 도착하기 전에 잡을 수 있습니다.
자동화 PIPELINE을 통해 배포하는 모바일 팀에게는 Capgo가 __CAPGO_KEEP_2__를 위한 CI/CD 설정에 대한 실용적인 가이드를 제공합니다. Capacitor 플러그인 상호작용을 테스트하십시오..
Capacitor __CAPGO_KEEP_1__을 위한 단위 테스트를 잘못된 방법으로는 플랫폼 브리지와 연결된 모든 서비스에 네이티브 플러그인을 직접 pull하는 것입니다.
The wrong way to unit test Capacitor code is to pull native plugins directly into every service. That couples your test suite to the platform bridge.
The better pattern is a thin abstraction:
// deviceStorage.js
async function saveFile(filesystem, path, data) {
return filesystem.writeFile({ path, data });
}
module.exports = { saveFile };
// draftService.js
async function persistDraft(storage, draft) {
await storage.save('draft.json', JSON.stringify(draft));
return { saved: true };
}
module.exports = { persistDraft };
// draftService.test.js
const { persistDraft } = require('./draftService');
test('persists a serialized draft', async () => {
const storage = {
save: jest.fn().mockResolvedValue(undefined)
};
const result = await persistDraft(storage, { title: 'Hello' });
expect(result).toEqual({ saved: true });
});
카메라 접근, 생체 인식 지시, 푸시 토큰 등록 및 네트워크 상태에 대한 아이디어는 동일합니다. 플러그인 호출은 어댑터에서 유지하고, 제어할 수 있는 인터페이스에 앱 로직을 테스트하세요.
Electron 메인 렌더러 및 IPC 테스트 code
Electron 앱에는 두 가지 중요한 구멍이 있습니다: 메인 프로세스 code 그리고 렌더러 프로세스 code. 테스트에서 그들을 흐리게 하지 마세요.
신뢰할 수 있는 설정은 일반적으로 분리됩니다:
- 뷰 모델, 상태, 형식, 및 UI 측면의 비즈니스 로직을 위한 렌더러 단위 테스트 뷰 모델, 상태, 형식, 및 UI 측면의 비즈니스 로직을 위한 메인 프로세스 단위 테스트
- __CAPGO_KEEP_0__ for menus, 파일 연산, 및 앱 생명 주기 결정
- IPC 계약 테스트 메시지 형태 및 예상 응답을위한
예제 IPC wrapper:
// ipcGateway.js
function sendSettings(ipcRenderer, payload) {
ipcRenderer.send('settings:update', payload);
}
module.exports = { sendSettings };
// ipcGateway.test.js
const { sendSettings } = require('./ipcGateway');
test('sends settings update over ipc', () => {
const ipcRenderer = { send: jest.fn() };
sendSettings(ipcRenderer, { theme: 'dark' });
expect(ipcRenderer.send).toHaveBeenCalledWith('settings:update', { theme: 'dark' });
});
내부 구현을 하나의 도우미에서 다른 도우미로 변경할 경우, 이 테스트는 여전히 유지됩니다. 왜냐하면 이 테스트는 중요하다고 여기는 동작을 검증하기 때문입니다. 데스크톱과 모바일에서 code의 표준입니다.
자바스크립트 단위 테스트에 대한 자주 묻는 질문
단위 통합 및 E2E 테스트 간의 차이점
A 단위 테스트 하나의 작은 논리 조각을 독립적으로 검사합니다. 통합 테스트 여러 구성 요소 또는 서비스가 올바르게 작동하는지 확인합니다. end-to-end 테스트 사용자 경로를 따라 실행 중인 애플리케이션을 테스트한다.
빠른 신뢰를 위해 비즈니스 규칙에 대한 단위 테스트를 사용하십시오. 저장소, 플러그인 wrapper 및 IPC와 같은 접합부에 대한 통합 테스트를 사용하십시오. E2E 테스트는 중요한 워크플로우가 깨질 경우 심각한 영향을 미치는 워크플로우에 대해 가볍게 사용하십시오.
완벽한 커버리지에 목표를 두어야 하나요.
아니요. 완벽한 커버리지가 팀을 저가 테스트로 밀어넣을 수 있습니다.
커버리지가 유용한 경우는 code에 위험한 부분이 nobody가 실행하지 않은 경우입니다. 그러나 엔지니어가 대시보드를 만족시키기 위해 얕은 주장만 추가하는 경우는 유용하지 않습니다. 만약 테스트 스위트가 약한 경우, 더 많은 커버리지로도 구원할 수 없습니다.
기존 코드베이스에 테스트를 추가하는 방법은 무엇인가요.
변경이 이미 발생하는 곳에서 시작하십시오. 테스트 전략의 대규모 재작성을 발표하지 말고 팀을 멈추지 마십시오.
실용적인 순서가 다음과 같습니다.
- 활성 code를 보호하십시오. 기능 작업 또는 버그 수정 중에 수정한 모듈에 테스트를 추가하십시오.
- 순수 논리를 추출하십시오. __CAPGO_KEEP_0__
- __CAPGO_KEEP_0__ __CAPGO_KEEP_0__
- __CAPGO_KEEP_0__ __CAPGO_KEEP_0__ __CAPGO_KEEP_0__ __CAPGO_KEEP_0__
__CAPGO_KEEP_0__
__CAPGO_KEEP_0__ Capacitor __CAPGO_KEEP_0__ __CAPGO_KEEP_0__ __CAPGO_KEEP_0__ Capgo __CAPGO_KEEP_0__