본문으로 건너뛰기

클라이언트 앱 테스트 가이드

작성일: 2026-04-06 Last Updated: 2026-04-06 대상: Browser Extension, Tauri Desktop, Flutter Mobile


목차

  1. 테스트 피라미드
  2. 플랫폼별 테스트 도구
  3. 스토어 심사 체크리스트 기반 테스트
  4. 접근성 테스트
  5. 성능 테스트
  6. 회귀 테스트 전략
  7. CI 자동 테스트
  8. 크로스 플랫폼 테스트 매트릭스

1. 테스트 피라미드

         /  E2E  \          ← 적게, 핵심 플로우만
/----------\
/ Integration \ ← 모듈 간 상호작용
/----------------\
/ Unit \ ← 많이, 빠르게
/--------------------\

계층별 비율 가이드

계층비율실행 시간주요 대상
Unit70%< 1분순수 함수, 유틸리티, 상태 관리, 비즈니스 로직
Integration20%< 5분API 연동, 스토리지, 모듈 간 통신
E2E10%< 15분핵심 사용자 플로우, 결제, 인증

2. 플랫폼별 테스트 도구

2.1 Browser Extension

계층도구설정 파일비고
UnitVitestvitest.config.ts빠른 실행, ESM 네이티브
IntegrationVitest + webextension-polyfill-mock-chrome.* API 모킹
E2EPlaywrightplaywright.config.ts실제 브라우저 + 확장 로드
// Vitest 설정 (Browser Extension)
import { defineConfig } from 'vitest/config';

export default defineConfig({
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./tests/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
thresholds: {
statements: 80,
branches: 75,
functions: 80,
lines: 80,
},
},
},
});
// Playwright E2E (확장 프로그램 로드)
import { test, chromium } from '@playwright/test';

test('extension popup loads', async () => {
const context = await chromium.launchPersistentContext('', {
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
],
});
// 확장 프로그램 테스트...
});

2.2 Tauri Desktop

계층도구비고
Unit (Rust)cargo test#[cfg(test)] 모듈
Unit (Frontend)VitestUI 컴포넌트
Integrationcargo test + tauri::testTauri 명령 테스트
E2EWebDriver (tauri-driver) 또는 PlaywrightWebView 기반
// Tauri 명령 단위 테스트
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_process_data() {
let result = process_data("input");
assert_eq!(result, "expected_output");
}
}

// Tauri 통합 테스트 (tauri::test)
#[cfg(test)]
mod integration_tests {
use tauri::test::{mock_builder, MockRuntime};

#[tauri::command]
async fn my_command() -> Result<String, String> {
Ok("result".into())
}

#[test]
fn test_tauri_command() {
let app = mock_builder()
.invoke_handler(tauri::generate_handler![my_command])
.build(tauri::test::mock_context(tauri::generate_context!()))
.unwrap();
// 명령 호출 테스트...
}
}

2.3 Flutter Mobile

계층도구디렉토리비고
Unitflutter_testtest/Widget 테스트 포함
Integrationflutter_test + mockitotest/서비스 통합
E2Eintegration_testintegration_test/실제 디바이스/에뮬레이터
// Unit 테스트 (flutter_test)
import 'package:flutter_test/flutter_test.dart';

void main() {
group('AuthService', () {
test('validates email format', () {
expect(AuthService.isValidEmail('user@example.com'), isTrue);
expect(AuthService.isValidEmail('invalid'), isFalse);
});
});
}
// Widget 테스트
testWidgets('Login button triggers auth', (tester) async {
await tester.pumpWidget(const MyApp());
await tester.enterText(find.byKey(Key('email')), 'user@test.com');
await tester.tap(find.byKey(Key('loginButton')));
await tester.pumpAndSettle();
expect(find.text('Welcome'), findsOneWidget);
});
// Integration 테스트 (integration_test/)
import 'package:integration_test/integration_test.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

testWidgets('full login flow', (tester) async {
app.main();
await tester.pumpAndSettle();
// 전체 로그인 플로우 테스트...
});
}

3. 스토어 심사 체크리스트 기반 테스트

3.1 Chrome Web Store 심사 기준 테스트

심사 항목테스트 방법자동화
단일 목적 원칙코드 리뷰 (불필요 권한 없음)수동
최소 권한 사용manifest.json 권한 검증 테스트O
Content Security PolicyCSP 헤더 검증O
원격 코드 실행 금지eval(), new Function() 사용 검사O
사용자 데이터 보호데이터 전송 암호화 테스트O
개인정보처리방침 링크URL 접근성 테스트O
// 권한 검증 테스트
describe('Manifest permissions', () => {
it('should only request necessary permissions', () => {
const manifest = JSON.parse(readFileSync('manifest.json', 'utf-8'));
const allowed = ['storage', 'activeTab', 'alarms'];
const extra = manifest.permissions.filter(
(p: string) => !allowed.includes(p)
);
expect(extra).toEqual([]);
});

it('should not use remote code', () => {
const csp = manifest.content_security_policy?.extension_pages;
expect(csp).not.toContain("'unsafe-eval'");
});
});

3.2 Google Play Store 심사 기준 테스트

심사 항목테스트 방법자동화
타겟 API 레벨build.gradle targetSdk 검증O
64bit 지원APK 분석 (arm64-v8a 포함)O
백그라운드 제한 준수서비스 사용 패턴 테스트O
권한 사용 근거런타임 권한 요청 테스트O
데이터 안전 섹션네트워크 요청 감사반자동
Deceptive behavior 없음UI 플로우 리뷰수동

3.3 Apple App Store 심사 기준 테스트

심사 항목테스트 방법자동화
App Transport SecurityHTTPS 전용 통신 검증O
개인정보 라벨 정확성네트워크 트래픽 감사반자동
앱 추적 투명성 (ATT)ATT 프롬프트 표시 테스트O
오프라인 기능네트워크 차단 상태 테스트O
크래시 없음모든 화면 네비게이션 E2EO
로그인 필수 시 테스트 계정리뷰 노트에 포함 확인수동

3.4 Firefox AMO 심사 기준 테스트

심사 항목테스트 방법자동화
소스 코드 심사 가능빌드 재현성 테스트O
난독화 금지빌드 출력 검사 (minify만 허용)O
외부 스크립트 로드 금지CSP + 네트워크 요청 검사O
사용자 동의 없는 데이터 수집 금지옵트인 UI 테스트O

4. 접근성 테스트

상세 가이드: accessibility.md

자동화 가능 항목

항목도구플랫폼
ARIA 라벨 검증axe-core, LighthouseExtension, Tauri
색상 대비axe-core전체
Tab 순서PlaywrightExtension, Tauri
스크린 리더 호환TalkBack/VoiceOver 수동Flutter
Semantics 트리flutter_test SemanticsFlutter
// axe-core 접근성 자동 테스트 (Playwright)
import AxeBuilder from '@axe-core/playwright';

test('homepage has no accessibility violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
// Flutter Semantics 테스트
testWidgets('has correct semantics', (tester) async {
await tester.pumpWidget(const MyButton(label: 'Submit'));
final semantics = tester.getSemantics(find.byType(MyButton));
expect(semantics.label, 'Submit');
expect(semantics.hasAction(SemanticsAction.tap), isTrue);
});

5. 성능 테스트

5.1 메모리 누수 검사

플랫폼도구방법
Browser ExtensionChrome DevTools Memory힙 스냅샷 비교 (반복 작업 전후)
TauriValgrind (Linux), Instruments (macOS)Rust 메모리 프로파일링
FlutterDevTools Memoryflutter run --profile + 메모리 그래프

5.2 성능 메트릭 기준

메트릭기준측정 방법
앱 시작 시간 (Cold)< 2초프로세스 시작 ~ 첫 화면 렌더
앱 시작 시간 (Warm)< 1초백그라운드 복귀 ~ 상호작용 가능
프레임 레이트>= 60fps (모바일), >= 30fps (데스크톱 최소)프레임 드롭 카운트
메모리 사용량< 150MB (모바일), < 300MB (데스크톱)피크 메모리
배터리 소모백그라운드 < 1%/hr배터리 모니터링
번들 크기Extension < 5MB, APK < 50MB빌드 출력 크기
네트워크 요청초기 로드 < 5개네트워크 탭 감사

5.3 번들 크기 분석

플랫폼도구명령
Browser Extensionwebpack-bundle-analyzer 또는 rollup-plugin-visualizer빌드 후 분석
Tauricargo bloatcargo bloat --release --crates
Flutterflutter build --analyze-size빌드 시 --analyze-size 플래그
AndroidAndroid Studio APK Analyzer.aab / .apk 분석

5.4 성능 회귀 감지 (CI)

# 번들 크기 체크 (GitHub Actions)
- name: Check bundle size
run: |
CURRENT_SIZE=$(stat -f%z dist/bundle.zip 2>/dev/null || stat -c%s dist/bundle.zip)
THRESHOLD=5242880 # 5MB
if [ "$CURRENT_SIZE" -gt "$THRESHOLD" ]; then
echo "::error::Bundle size ($CURRENT_SIZE bytes) exceeds threshold ($THRESHOLD bytes)"
exit 1
fi

6. 회귀 테스트 전략

6.1 회귀 테스트 범위

영역테스트 항목우선순위
인증/로그인로그인, 로그아웃, 토큰 갱신, 세션 만료P0
데이터 동기화로컬-서버 동기화, 충돌 해결P0
결제/구독구매, 복원, 구독 상태 확인P0
핵심 기능각 앱의 주요 비즈니스 로직P0
UI 렌더링주요 화면 레이아웃P1
오프라인 모드네트워크 끊김 시 동작P1
업데이트마이그레이션, 데이터 보존P1
알림푸시, 로컬 알림P2
딥링크URL 스킴, 유니버설 링크P2

6.2 스냅샷 테스트

플랫폼도구비고
Browser ExtensionPlaywright toMatchSnapshot()페이지 스크린샷 비교
TauriPlaywright toMatchSnapshot()WebView 스크린샷
Fluttergolden_toolkitWidget 골든 파일 비교
// Flutter 골든 테스트
testWidgets('matches golden', (tester) async {
await tester.pumpWidget(const MyWidget());
await expectLater(
find.byType(MyWidget),
matchesGoldenFile('goldens/my_widget.png'),
);
});

6.3 회귀 테스트 실행 시점

시점범위자동화
PR 생성변경 모듈 관련 테스트O
develop 병합전체 Unit + IntegrationO
release 브랜치전체 (Unit + Integration + E2E)O
핫픽스수정 관련 + P0 회귀O

7. CI 자동 테스트

7.1 GitHub Actions 테스트 워크플로우

name: Test
on: [push, pull_request]

jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup
# 플랫폼별 환경 설정...
- name: Run unit tests
run: |
# Browser Extension
npm test -- --coverage

# Tauri (Rust)
cargo test --workspace

# Flutter
flutter test --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4

integration-test:
needs: unit-test
runs-on: ubuntu-latest
steps:
- name: Run integration tests
run: |
npm run test:integration
# 또는 cargo test --test integration_*
# 또는 flutter test test/integration/

e2e-test:
needs: integration-test
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Run E2E tests
run: npx playwright test

7.2 테스트 캐싱 전략

플랫폼캐시 대상
Browser Extensionnode_modules/package-lock.json 해시
Tauri~/.cargo/registry, target/Cargo.lock 해시
Flutter~/.pub-cache/pubspec.lock 해시

7.3 테스트 실패 시 대응

- name: Upload test artifacts on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.os }}
path: |
test-results/
playwright-report/
coverage/

8. 크로스 플랫폼 테스트 매트릭스

8.1 Browser Extension

브라우저버전OS우선순위
ChromeLatest, Latest-1Win, Mac, LinuxP0
FirefoxLatest, Latest-1Win, Mac, LinuxP0
EdgeLatestWinP1
BraveLatestWin, MacP2

8.2 Tauri Desktop

OS버전아키텍처우선순위
Windows10, 11x64P0
macOS13 (Ventura)+, 14 (Sonoma)+ARM64 (M1+)P0
macOS13+x64 (Intel)P1
Ubuntu22.04, 24.04x64P1
FedoraLatestx64P2

8.3 Flutter Mobile

OS버전디바이스우선순위
iOS16+, 17+iPhone 13+, SE 3rdP0
iOS15iPhone 11P1
Android13 (API 33)+Pixel 6+, Samsung S22+P0
Android11 (API 30)+중저가 (4GB RAM)P1
Android10 (API 29)구형 디바이스P2

8.4 테스트 디바이스 관리

방법장점단점
실제 디바이스가장 정확비용, 관리 부담
Firebase Test LabAndroid 실기기 클라우드비용 발생
BrowserStack모든 플랫폼비용 발생
에뮬레이터/시뮬레이터무료, CI 호환실기기와 차이

부록: 테스트 전체 체크리스트

테스트 커버리지 기준

항목최소 기준권장 기준
Statement 커버리지70%80%
Branch 커버리지65%75%
Function 커버리지70%80%
P0 시나리오 E2E100%100%

테스트 작성 규칙

규칙설명
AAA 패턴Arrange - Act - Assert 구조
독립적 실행테스트 간 상태 공유 금지
명확한 이름should_[행동]_when_[조건] 형식
모킹 최소화외부 의존성만 모킹, 내부 로직은 실제 실행
플레이키 방지타이머, 네트워크 의존 테스트는 모킹 또는 재시도
빠른 피드백Unit 테스트는 1초 이내 실행