클라이언트 앱 테스트 가이드
작성일: 2026-04-06 Last Updated: 2026-04-06 대상: Browser Extension, Tauri Desktop, Flutter Mobile
목차
1. 테스트 피라미드
/ E2E \ ← 적게, 핵심 플로우만
/----------\
/ Integration \ ← 모듈 간 상호작용
/----------------\
/ Unit \ ← 많이, 빠르게
/--------------------\
계층별 비율 가이드
| 계층 | 비율 | 실행 시간 | 주요 대상 |
|---|---|---|---|
| Unit | 70% | < 1분 | 순수 함수, 유틸리티, 상태 관리, 비즈니스 로직 |
| Integration | 20% | < 5분 | API 연동, 스토리지, 모듈 간 통신 |
| E2E | 10% | < 15분 | 핵심 사용자 플로우, 결제, 인증 |
2. 플랫폼별 테스트 도구
2.1 Browser Extension
| 계층 | 도구 | 설정 파일 | 비고 |
|---|---|---|---|
| Unit | Vitest | vitest.config.ts | 빠른 실행, ESM 네이티브 |
| Integration | Vitest + webextension-polyfill-mock | - | chrome.* API 모킹 |
| E2E | Playwright | playwright.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) | Vitest | UI 컴포넌트 |
| Integration | cargo test + tauri::test | Tauri 명령 테스트 |
| E2E | WebDriver (tauri-driver) 또는 Playwright | WebView 기반 |
// 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
| 계층 | 도구 | 디렉토리 | 비고 |
|---|---|---|---|
| Unit | flutter_test | test/ | Widget 테스트 포함 |
| Integration | flutter_test + mockito | test/ | 서비스 통합 |
| E2E | integration_test | integration_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 Policy | CSP 헤더 검증 | 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 Security | HTTPS 전용 통신 검증 | O |
| 개인정보 라벨 정확성 | 네트워크 트래픽 감사 | 반자동 |
| 앱 추적 투명성 (ATT) | ATT 프롬프트 표시 테스트 | O |
| 오프라인 기능 | 네트워크 차단 상태 테스트 | O |
| 크 래시 없음 | 모든 화면 네비게이션 E2E | O |
| 로그인 필수 시 테스트 계정 | 리뷰 노트에 포함 확인 | 수동 |
3.4 Firefox AMO 심사 기준 테스트
| 심사 항목 | 테스트 방법 | 자동화 |
|---|---|---|
| 소스 코드 심사 가능 | 빌드 재현성 테스트 | O |
| 난독화 금지 | 빌드 출력 검사 (minify만 허용) | O |
| 외부 스크립트 로드 금지 | CSP + 네트워크 요청 검사 | O |
| 사용자 동의 없는 데이터 수집 금지 | 옵트인 UI 테스트 | O |
4. 접근성 테스트
상세 가이드:
accessibility.md
자동화 가능 항목
| 항목 |
|---|