test: 테스트 자동화 구현 및 Mock 서비스 오류 수정
- 테스트 패키지 추가 (mockito, golden_toolkit, patrol 등) - 테스트 가이드 문서 작성 (TEST_GUIDE.md) - 테스트 진행 상황 문서 작성 (TEST_PROGRESS.md) - 테스트 헬퍼 클래스 구현 - test_helpers.dart: 기본 테스트 유틸리티 - mock_data_helpers.dart: Mock 데이터 생성 헬퍼 - mock_services.dart: Mock 서비스 설정 (오류 수정 완료) - simple_mock_services.dart: 간단한 Mock 서비스 - 단위 테스트 구현 - CompanyListController 테스트 - EquipmentListController 테스트 - UserListController 테스트 - Widget 테스트 구현 (CompanyListScreen) Mock 서비스 주요 수정사항: - dartz import 추가 - Either 타입 제거 (실제 서비스와 일치하도록) - 메서드 시그니처 수정 (실제 서비스 인터페이스와 일치) - Mock 데이터 생성 메서드 추가 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
215
TEST_GUIDE.md
Normal file
215
TEST_GUIDE.md
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
# Flutter 테스트 자동화 가이드
|
||||||
|
|
||||||
|
## 📋 개요
|
||||||
|
이 문서는 Flutter 앱의 테스트 자동화를 위한 가이드입니다. 각 화면의 버튼 클릭, 서버 통신, 데이터 입력/수정/저장 등 모든 액션에 대한 테스트를 포함합니다.
|
||||||
|
|
||||||
|
## 🏗️ 테스트 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
test/
|
||||||
|
├── helpers/ # 테스트 헬퍼 클래스
|
||||||
|
│ ├── test_helpers.dart # 기본 테스트 헬퍼
|
||||||
|
│ ├── mock_data_helpers.dart # Mock 데이터 생성 헬퍼
|
||||||
|
│ └── simple_mock_services.dart # Mock 서비스 설정
|
||||||
|
├── unit/ # 단위 테스트
|
||||||
|
│ └── controllers/ # 컨트롤러 테스트
|
||||||
|
├── widget/ # Widget 테스트
|
||||||
|
│ └── screens/ # 화면별 Widget 테스트
|
||||||
|
└── integration/ # 통합 테스트
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 테스트 환경 설정
|
||||||
|
|
||||||
|
### 1. 필요한 패키지 (pubspec.yaml)
|
||||||
|
```yaml
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
integration_test:
|
||||||
|
sdk: flutter
|
||||||
|
mockito: ^5.4.5
|
||||||
|
build_runner: ^2.4.9
|
||||||
|
golden_toolkit: ^0.15.0
|
||||||
|
mocktail: ^1.0.3
|
||||||
|
fake_async: ^1.3.1
|
||||||
|
test: ^1.25.2
|
||||||
|
coverage: ^1.7.2
|
||||||
|
patrol: ^3.6.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Mock 클래스 생성
|
||||||
|
```bash
|
||||||
|
flutter pub run build_runner build --delete-conflicting-outputs
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 테스트 작성 예제
|
||||||
|
|
||||||
|
### 1. 컨트롤러 단위 테스트
|
||||||
|
```dart
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late CompanyListController controller;
|
||||||
|
late MockMockDataService mockDataService;
|
||||||
|
late MockCompanyService mockCompanyService;
|
||||||
|
late GetIt getIt;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
getIt = setupTestGetIt();
|
||||||
|
mockDataService = MockMockDataService();
|
||||||
|
mockCompanyService = MockCompanyService();
|
||||||
|
|
||||||
|
// GetIt에 서비스 등록
|
||||||
|
getIt.registerSingleton<CompanyService>(mockCompanyService);
|
||||||
|
|
||||||
|
// Mock 설정
|
||||||
|
SimpleMockServiceHelpers.setupMockDataServiceMock(mockDataService);
|
||||||
|
SimpleMockServiceHelpers.setupCompanyServiceMock(mockCompanyService);
|
||||||
|
|
||||||
|
controller = CompanyListController(dataService: mockDataService);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
controller.dispose();
|
||||||
|
getIt.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
group('CompanyListController 테스트', () {
|
||||||
|
test('검색 기능 테스트', () async {
|
||||||
|
await controller.updateSearchKeyword('테스트');
|
||||||
|
expect(controller.searchKeyword, '테스트');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Widget 테스트
|
||||||
|
```dart
|
||||||
|
testWidgets('화면 렌더링 테스트', (WidgetTester tester) async {
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
expect(find.text('회사 관리'), findsOneWidget);
|
||||||
|
expect(find.byType(TextField), findsOneWidget);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Mock 데이터 생성
|
||||||
|
```dart
|
||||||
|
// 회사 목록 생성
|
||||||
|
final companies = MockDataHelpers.createMockCompanyList(count: 5);
|
||||||
|
|
||||||
|
// 특정 회사 생성
|
||||||
|
final company = MockDataHelpers.createMockCompany(
|
||||||
|
id: 1,
|
||||||
|
name: '테스트 회사',
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 테스트 전략
|
||||||
|
|
||||||
|
### 1. 단위 테스트 (Unit Tests)
|
||||||
|
- **대상**: 컨트롤러, 서비스, 유틸리티 함수
|
||||||
|
- **목적**: 개별 컴포넌트의 로직 검증
|
||||||
|
- **실행**: `flutter test test/unit/`
|
||||||
|
|
||||||
|
### 2. Widget 테스트
|
||||||
|
- **대상**: 개별 화면 및 위젯
|
||||||
|
- **목적**: UI 렌더링 및 상호작용 검증
|
||||||
|
- **실행**: `flutter test test/widget/`
|
||||||
|
|
||||||
|
### 3. 통합 테스트 (Integration Tests)
|
||||||
|
- **대상**: 전체 사용자 플로우
|
||||||
|
- **목적**: 실제 앱 동작 검증
|
||||||
|
- **실행**: `flutter test integration_test/`
|
||||||
|
|
||||||
|
## 🔍 주요 테스트 케이스
|
||||||
|
|
||||||
|
### 화면별 필수 테스트
|
||||||
|
1. **초기 렌더링**: 화면이 올바르게 표시되는지 확인
|
||||||
|
2. **데이터 로딩**: API 호출 및 데이터 표시 확인
|
||||||
|
3. **사용자 입력**: 텍스트 입력, 버튼 클릭 등
|
||||||
|
4. **네비게이션**: 화면 전환 동작 확인
|
||||||
|
5. **에러 처리**: 네트워크 오류, 유효성 검사 실패 등
|
||||||
|
6. **상태 관리**: 로딩, 성공, 실패 상태 전환
|
||||||
|
|
||||||
|
## 🚨 주의사항
|
||||||
|
|
||||||
|
### 1. 모델 불일치 문제
|
||||||
|
- 실제 모델과 Mock 모델의 구조가 일치하는지 확인
|
||||||
|
- 특히 `Address`, `Company`, `User` 모델 주의
|
||||||
|
|
||||||
|
### 2. 서비스 시그니처
|
||||||
|
- Mock 서비스의 메서드 시그니처가 실제 서비스와 일치해야 함
|
||||||
|
- 반환 타입 특히 주의 (예: `User` vs `AuthUser`)
|
||||||
|
|
||||||
|
### 3. GetIt 설정
|
||||||
|
- 테스트 전 반드시 GetIt 초기화
|
||||||
|
- 테스트 후 반드시 GetIt reset
|
||||||
|
|
||||||
|
## 📊 테스트 커버리지
|
||||||
|
|
||||||
|
### 커버리지 확인
|
||||||
|
```bash
|
||||||
|
flutter test --coverage
|
||||||
|
genhtml coverage/lcov.info -o coverage/html
|
||||||
|
open coverage/html/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### 목표 커버리지
|
||||||
|
- 단위 테스트: 80% 이상
|
||||||
|
- Widget 테스트: 70% 이상
|
||||||
|
- 통합 테스트: 주요 사용자 시나리오 100%
|
||||||
|
|
||||||
|
## 🔄 CI/CD 통합
|
||||||
|
|
||||||
|
### GitHub Actions 예제
|
||||||
|
```yaml
|
||||||
|
name: Test
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: subosito/flutter-action@v2
|
||||||
|
- run: flutter pub get
|
||||||
|
- run: flutter test
|
||||||
|
- run: flutter test --coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 추가 리소스
|
||||||
|
- [Flutter Testing Documentation](https://flutter.dev/docs/testing)
|
||||||
|
- [Mockito Documentation](https://pub.dev/packages/mockito)
|
||||||
|
- [GetIt Documentation](https://pub.dev/packages/get_it)
|
||||||
|
|
||||||
|
## 🔧 문제 해결
|
||||||
|
|
||||||
|
### Mock 클래스를 찾을 수 없을 때
|
||||||
|
```bash
|
||||||
|
flutter pub run build_runner build --delete-conflicting-outputs
|
||||||
|
```
|
||||||
|
|
||||||
|
### 테스트가 타임아웃될 때
|
||||||
|
`pumpAndSettleWithTimeout` 헬퍼 사용:
|
||||||
|
```dart
|
||||||
|
await pumpAndSettleWithTimeout(tester, timeout: Duration(seconds: 10));
|
||||||
|
```
|
||||||
|
|
||||||
|
### GetIt 관련 오류
|
||||||
|
```dart
|
||||||
|
setUp(() {
|
||||||
|
getIt = setupTestGetIt(); // 반드시 첫 번째로 실행
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
getIt.reset(); // 반드시 실행
|
||||||
|
});
|
||||||
|
```
|
||||||
200
TEST_PROGRESS.md
Normal file
200
TEST_PROGRESS.md
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
# Flutter 테스트 자동화 진행 상황
|
||||||
|
|
||||||
|
## 📅 작업 요약
|
||||||
|
- **목표**: 각 화면의 버튼 클릭, 서버 통신, 데이터 입력/수정/저장 등 모든 액션에 대한 테스트 자동화
|
||||||
|
- **진행 상황**: Phase 2 진행 중 (Widget 테스트 구현)
|
||||||
|
|
||||||
|
## ✅ 완료된 작업
|
||||||
|
|
||||||
|
### 1. Phase 1: 프로젝트 분석 및 설정 (완료)
|
||||||
|
- ✅ 코드베이스 분석 및 프로젝트 구조 파악
|
||||||
|
- ✅ 모든 화면(Screen/Page) 파일 식별 및 목록화
|
||||||
|
- ✅ API 서비스 및 네트워크 통신 구조 분석
|
||||||
|
- ✅ 테스트 패키지 설치 및 환경 설정
|
||||||
|
|
||||||
|
### 2. 테스트 인프라 구축 (완료)
|
||||||
|
- ✅ 테스트 디렉토리 구조 설계
|
||||||
|
- ✅ 테스트 헬퍼 클래스 생성
|
||||||
|
- `test_helpers.dart`: 기본 테스트 유틸리티
|
||||||
|
- `mock_data_helpers.dart`: Mock 데이터 생성
|
||||||
|
- `simple_mock_services.dart`: Mock 서비스 설정
|
||||||
|
- ✅ Mock 클래스 생성 (build_runner 사용)
|
||||||
|
|
||||||
|
### 3. 단위 테스트 구현 (진행 중)
|
||||||
|
#### CompanyListController 테스트 ✅
|
||||||
|
- 검색 키워드 업데이트
|
||||||
|
- 회사 선택/해제
|
||||||
|
- 전체 선택/해제
|
||||||
|
- 필터 적용
|
||||||
|
- 회사 삭제
|
||||||
|
- 에러 처리
|
||||||
|
|
||||||
|
#### EquipmentListController 테스트 ✅
|
||||||
|
- 장비 선택/해제
|
||||||
|
- 전체 선택
|
||||||
|
- 상태 필터 변경
|
||||||
|
- 장비 삭제
|
||||||
|
- 선택된 장비 수 계산
|
||||||
|
- 에러 처리
|
||||||
|
|
||||||
|
#### UserListController 테스트 ✅
|
||||||
|
- 초기 상태 확인
|
||||||
|
- 사용자 목록 로드
|
||||||
|
- 검색 쿼리 설정 및 검색
|
||||||
|
- 필터 설정 (회사, 권한, 활성 상태)
|
||||||
|
- 필터 초기화
|
||||||
|
- 사용자 삭제
|
||||||
|
- 사용자 상태 변경
|
||||||
|
- 페이지네이션 (더 불러오기)
|
||||||
|
- Mock 모드 필터링
|
||||||
|
- 지점명 조회
|
||||||
|
- 에러 처리
|
||||||
|
|
||||||
|
### 4. 문서화 (완료)
|
||||||
|
- ✅ `TEST_GUIDE.md`: 테스트 작성 가이드
|
||||||
|
- ✅ `TEST_PROGRESS.md`: 진행 상황 문서 (현재 문서)
|
||||||
|
|
||||||
|
## 🔧 해결된 주요 이슈
|
||||||
|
|
||||||
|
### 1. 모델 불일치 문제
|
||||||
|
- **문제**: Mock 데이터 모델과 실제 프로젝트 모델 구조 차이
|
||||||
|
- **해결**:
|
||||||
|
- Address 모델: streetAddress → zipCode/region/detailAddress
|
||||||
|
- User 모델: companyId 필수 파라미터 추가
|
||||||
|
- AuthUser vs User 타입 정리
|
||||||
|
|
||||||
|
### 2. 서비스 시그니처 불일치
|
||||||
|
- **문제**: Mock 서비스 메서드와 실제 서비스 메서드 시그니처 차이
|
||||||
|
- **해결**:
|
||||||
|
- CompanyService.getCompanies 파라미터 수정
|
||||||
|
- EquipmentService 반환 타입 정리
|
||||||
|
- Clean Architecture 패턴 제거 (Either → 직접 반환)
|
||||||
|
|
||||||
|
### 3. Controller 메서드명 차이
|
||||||
|
- **문제**: 테스트에서 사용한 메서드가 실제 컨트롤러에 없음
|
||||||
|
- **해결**:
|
||||||
|
- toggleAllSelection → toggleSelectAll
|
||||||
|
- toggleEquipmentSelection → selectEquipment
|
||||||
|
|
||||||
|
## 📋 남은 작업
|
||||||
|
|
||||||
|
### Phase 2: Widget 테스트 구현 (진행 중)
|
||||||
|
- [ ] 사용자 관리 화면 Widget 테스트
|
||||||
|
- [ ] 라이선스 관리 화면 Widget 테스트
|
||||||
|
- [ ] 창고 관리 화면 Widget 테스트
|
||||||
|
- [ ] 대시보드 화면 Widget 테스트
|
||||||
|
|
||||||
|
### Phase 2: Integration 테스트
|
||||||
|
- [ ] 로그인 플로우 테스트
|
||||||
|
- [ ] 회사 등록/수정/삭제 플로우
|
||||||
|
- [ ] 장비 입고/출고 플로우
|
||||||
|
- [ ] 사용자 관리 플로우
|
||||||
|
|
||||||
|
### Phase 3: CI/CD 및 고급 기능
|
||||||
|
- [ ] GitHub Actions 설정
|
||||||
|
- [ ] 테스트 커버리지 리포트
|
||||||
|
- [ ] E2E 테스트 (Patrol 사용)
|
||||||
|
- [ ] 성능 테스트
|
||||||
|
|
||||||
|
## 🛠️ 사용된 기술 스택
|
||||||
|
|
||||||
|
### 테스트 프레임워크
|
||||||
|
```yaml
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
mockito: ^5.4.5
|
||||||
|
build_runner: ^2.4.9
|
||||||
|
get_it: ^7.7.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 테스트 구조
|
||||||
|
```
|
||||||
|
test/
|
||||||
|
├── helpers/
|
||||||
|
│ ├── test_helpers.dart
|
||||||
|
│ ├── mock_data_helpers.dart
|
||||||
|
│ ├── simple_mock_services.dart
|
||||||
|
│ └── simple_mock_services.mocks.dart
|
||||||
|
├── unit/
|
||||||
|
│ └── controllers/
|
||||||
|
│ ├── company_list_controller_test.dart
|
||||||
|
│ ├── equipment_list_controller_test.dart
|
||||||
|
│ └── user_list_controller_test.dart
|
||||||
|
├── widget/
|
||||||
|
│ └── screens/
|
||||||
|
└── integration/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 💡 다음 단계 추천
|
||||||
|
|
||||||
|
1. **Widget 테스트 구현**
|
||||||
|
- UserListScreen Widget 테스트
|
||||||
|
- CompanyListScreen Widget 테스트
|
||||||
|
- EquipmentListScreen Widget 테스트
|
||||||
|
|
||||||
|
2. **Integration 테스트 시작**
|
||||||
|
- 주요 사용자 시나리오 정의
|
||||||
|
- 화면 간 네비게이션 테스트
|
||||||
|
|
||||||
|
3. **API Mock 서버 구축**
|
||||||
|
- 실제 API 호출 테스트
|
||||||
|
- 네트워크 에러 시나리오
|
||||||
|
|
||||||
|
## 📝 참고 사항
|
||||||
|
|
||||||
|
### GetIt 사용 시 주의점
|
||||||
|
```dart
|
||||||
|
setUp(() {
|
||||||
|
getIt = setupTestGetIt(); // 반드시 첫 번째로
|
||||||
|
// Mock 서비스 등록
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
getIt.reset(); // 반드시 실행
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mock 데이터 생성
|
||||||
|
```dart
|
||||||
|
// Company 목록
|
||||||
|
final companies = MockDataHelpers.createMockCompanyList(count: 5);
|
||||||
|
|
||||||
|
// UnifiedEquipment 생성
|
||||||
|
final equipment = MockDataHelpers.createMockUnifiedEquipment(
|
||||||
|
id: 1,
|
||||||
|
name: '노트북',
|
||||||
|
status: 'I', // 입고 상태
|
||||||
|
);
|
||||||
|
|
||||||
|
// User 모델 생성
|
||||||
|
final user = MockDataHelpers.createMockUserModel(
|
||||||
|
id: 1,
|
||||||
|
name: '테스트 사용자',
|
||||||
|
role: 'S', // S: 관리자, M: 멤버
|
||||||
|
);
|
||||||
|
|
||||||
|
// User 목록 생성
|
||||||
|
final users = MockDataHelpers.createMockUserModelList(count: 10);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 테스트 실행
|
||||||
|
```bash
|
||||||
|
# 모든 테스트 실행
|
||||||
|
flutter test
|
||||||
|
|
||||||
|
# 특정 파일 테스트
|
||||||
|
flutter test test/unit/controllers/company_list_controller_test.dart
|
||||||
|
|
||||||
|
# 커버리지 포함
|
||||||
|
flutter test --coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔗 관련 문서
|
||||||
|
- [TEST_GUIDE.md](./TEST_GUIDE.md) - 테스트 작성 가이드
|
||||||
|
- [CLAUDE.md](./CLAUDE.md) - 프로젝트 개발 규칙
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
이 문서는 지속적으로 업데이트됩니다.
|
||||||
|
마지막 업데이트: 2025-07-31
|
||||||
179
pubspec.lock
179
pubspec.lock
@@ -150,6 +150,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.4"
|
version: "2.0.4"
|
||||||
|
cli_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cli_config
|
||||||
|
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -182,6 +190,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
|
coverage:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: coverage
|
||||||
|
sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.15.0"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -222,8 +238,24 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
fake_async:
|
dispose_scope:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dispose_scope
|
||||||
|
sha256: "48ec38ca2631c53c4f8fa96b294c801e55c335db5e3fb9f82cede150cfe5a2af"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
equatable:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
|
fake_async:
|
||||||
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||||
@@ -267,6 +299,11 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.2.1"
|
version: "5.2.1"
|
||||||
|
flutter_driver:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -370,6 +407,11 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "4.0.0"
|
||||||
|
fuchsia_remote_debug_protocol:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
get_it:
|
get_it:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -386,6 +428,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
|
golden_toolkit:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: golden_toolkit
|
||||||
|
sha256: "8f74adab33154fe7b731395782797021f97d2edc52f7bfb85ff4f1b5c4a215f0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.0"
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -450,6 +500,11 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.6.2"
|
version: "2.6.2"
|
||||||
|
integration_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -578,6 +633,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.5"
|
version: "5.4.5"
|
||||||
|
mocktail:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: mocktail
|
||||||
|
sha256: "890df3f9688106f25755f26b1c60589a92b3ab91a22b8b224947ad041bf172d8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -586,6 +649,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
node_preamble:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: node_preamble
|
||||||
|
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -658,6 +729,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
patrol:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: patrol
|
||||||
|
sha256: "8432f7d71f5d40788804f1a5662f082dc10c6c991ec1160c0cd6e4ecf1aed542"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.18.0"
|
||||||
|
patrol_finders:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: patrol_finders
|
||||||
|
sha256: "4a658d7d560de523f92deb3fa3326c78747ca0bf7e7f4b8788c012463138b628"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.9.0"
|
||||||
|
patrol_log:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: patrol_log
|
||||||
|
sha256: "9fed4143980df1e3bbcfa00d0b443c7d68f04f9132317b7698bbc37f8a5a58c5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.0"
|
||||||
pdf:
|
pdf:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -722,6 +817,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.14.2"
|
version: "5.14.2"
|
||||||
|
process:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: process
|
||||||
|
sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.3"
|
||||||
protobuf:
|
protobuf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -794,6 +897,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.2"
|
version: "1.4.2"
|
||||||
|
shelf_packages_handler:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_packages_handler
|
||||||
|
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
|
shelf_static:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_static
|
||||||
|
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.3"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -823,6 +942,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.5"
|
version: "1.3.5"
|
||||||
|
source_map_stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_map_stack_trace
|
||||||
|
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
source_maps:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_maps
|
||||||
|
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.13"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -863,6 +998,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
version: "1.4.1"
|
||||||
|
sync_http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sync_http
|
||||||
|
sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -871,6 +1014,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.2"
|
version: "1.2.2"
|
||||||
|
test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: test
|
||||||
|
sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.25.15"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -879,6 +1030,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.4"
|
version: "0.7.4"
|
||||||
|
test_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_core
|
||||||
|
sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.8"
|
||||||
timing:
|
timing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -983,6 +1142,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.3"
|
||||||
|
webdriver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: webdriver
|
||||||
|
sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
webkit_inspection_protocol:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: webkit_inspection_protocol
|
||||||
|
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1017,4 +1192,4 @@ packages:
|
|||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.8.0 <4.0.0"
|
dart: ">=3.8.0 <4.0.0"
|
||||||
flutter: ">=3.27.0"
|
flutter: ">=3.32.0"
|
||||||
|
|||||||
12
pubspec.yaml
12
pubspec.yaml
@@ -45,6 +45,8 @@ dependencies:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
integration_test:
|
||||||
|
sdk: flutter
|
||||||
flutter_lints: ^5.0.0
|
flutter_lints: ^5.0.0
|
||||||
|
|
||||||
# 코드 생성
|
# 코드 생성
|
||||||
@@ -55,6 +57,16 @@ dev_dependencies:
|
|||||||
freezed: ^2.4.6
|
freezed: ^2.4.6
|
||||||
mockito: ^5.4.5
|
mockito: ^5.4.5
|
||||||
|
|
||||||
|
# 테스트 도구
|
||||||
|
golden_toolkit: ^0.15.0
|
||||||
|
mocktail: ^1.0.3
|
||||||
|
fake_async: ^1.3.1
|
||||||
|
test: ^1.25.2
|
||||||
|
coverage: ^1.7.2
|
||||||
|
|
||||||
|
# UI 테스트
|
||||||
|
patrol: ^3.6.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
assets:
|
assets:
|
||||||
|
|||||||
614
test/helpers/mock_data_helpers.dart
Normal file
614
test/helpers/mock_data_helpers.dart
Normal file
@@ -0,0 +1,614 @@
|
|||||||
|
import 'package:superport/data/models/auth/auth_user.dart';
|
||||||
|
import 'package:superport/data/models/auth/login_response.dart';
|
||||||
|
import 'package:superport/data/models/company/company_dto.dart' as company_dto;
|
||||||
|
// import 'package:superport/data/models/company/branch_dto.dart';
|
||||||
|
import 'package:superport/data/models/equipment/equipment_response.dart';
|
||||||
|
import 'package:superport/data/models/equipment/equipment_io_response.dart';
|
||||||
|
import 'package:superport/data/models/user/user_dto.dart';
|
||||||
|
import 'package:superport/data/models/license/license_dto.dart';
|
||||||
|
import 'package:superport/data/models/warehouse/warehouse_dto.dart';
|
||||||
|
import 'package:superport/data/models/common/paginated_response.dart';
|
||||||
|
import 'package:superport/models/company_model.dart';
|
||||||
|
import 'package:superport/models/address_model.dart';
|
||||||
|
import 'package:superport/models/equipment_unified_model.dart';
|
||||||
|
import 'package:superport/models/user_model.dart';
|
||||||
|
import 'package:superport/models/warehouse_location_model.dart';
|
||||||
|
import 'package:superport/models/license_model.dart';
|
||||||
|
|
||||||
|
/// 테스트용 Mock 데이터 생성 헬퍼
|
||||||
|
class MockDataHelpers {
|
||||||
|
/// Mock 사용자 생성
|
||||||
|
static AuthUser createMockUser({
|
||||||
|
int id = 1,
|
||||||
|
String username = 'testuser',
|
||||||
|
String email = 'test@example.com',
|
||||||
|
String name = '테스트 사용자',
|
||||||
|
String role = 'USER',
|
||||||
|
}) {
|
||||||
|
return AuthUser(
|
||||||
|
id: id,
|
||||||
|
username: username,
|
||||||
|
email: email,
|
||||||
|
name: name,
|
||||||
|
role: role,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 로그인 응답 생성
|
||||||
|
static LoginResponse createMockLoginResponse({
|
||||||
|
String accessToken = 'test_access_token',
|
||||||
|
String refreshToken = 'test_refresh_token',
|
||||||
|
String tokenType = 'Bearer',
|
||||||
|
int expiresIn = 3600,
|
||||||
|
AuthUser? user,
|
||||||
|
}) {
|
||||||
|
return LoginResponse(
|
||||||
|
accessToken: accessToken,
|
||||||
|
refreshToken: refreshToken,
|
||||||
|
tokenType: tokenType,
|
||||||
|
expiresIn: expiresIn,
|
||||||
|
user: user ?? createMockUser(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 회사 생성
|
||||||
|
static Company createMockCompany({
|
||||||
|
int id = 1,
|
||||||
|
String name = '테스트 회사',
|
||||||
|
String? contactName,
|
||||||
|
String? contactPosition,
|
||||||
|
String? contactPhone,
|
||||||
|
String? contactEmail,
|
||||||
|
String? addressStr,
|
||||||
|
String? remark,
|
||||||
|
List<CompanyType>? companyTypes,
|
||||||
|
}) {
|
||||||
|
return Company(
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
address: Address(
|
||||||
|
zipCode: '06234',
|
||||||
|
region: addressStr ?? '서울특별시 강남구 테헤란로 123',
|
||||||
|
detailAddress: '테스트빌딩 5층',
|
||||||
|
),
|
||||||
|
contactName: contactName ?? '홍길동',
|
||||||
|
contactPosition: contactPosition ?? '대표이사',
|
||||||
|
contactPhone: contactPhone ?? '02-1234-5678',
|
||||||
|
contactEmail: contactEmail ?? 'company@test.com',
|
||||||
|
companyTypes: companyTypes ?? [CompanyType.customer],
|
||||||
|
remark: remark,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 지점 생성
|
||||||
|
static Branch createMockBranch({
|
||||||
|
int id = 1,
|
||||||
|
int companyId = 1,
|
||||||
|
String name = '본사',
|
||||||
|
String? addressStr,
|
||||||
|
String? contactName,
|
||||||
|
String? contactPosition,
|
||||||
|
String? contactPhone,
|
||||||
|
String? contactEmail,
|
||||||
|
}) {
|
||||||
|
return Branch(
|
||||||
|
id: id,
|
||||||
|
companyId: companyId,
|
||||||
|
name: name,
|
||||||
|
address: Address(
|
||||||
|
zipCode: '06234',
|
||||||
|
region: addressStr ?? '서울특별시 강남구 테헤란로 123',
|
||||||
|
detailAddress: '테스트빌딩 5층',
|
||||||
|
),
|
||||||
|
contactName: contactName ?? '김지점',
|
||||||
|
contactPosition: contactPosition ?? '지점장',
|
||||||
|
contactPhone: contactPhone ?? '02-1234-5678',
|
||||||
|
contactEmail: contactEmail ?? 'branch@test.com',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 장비 생성
|
||||||
|
static EquipmentResponse createMockEquipment({
|
||||||
|
int id = 1,
|
||||||
|
String equipmentNumber = 'EQ001',
|
||||||
|
String? category1,
|
||||||
|
String? category2,
|
||||||
|
String? category3,
|
||||||
|
String manufacturer = '삼성전자',
|
||||||
|
String? modelName,
|
||||||
|
String? serialNumber,
|
||||||
|
String? barcode,
|
||||||
|
DateTime? purchaseDate,
|
||||||
|
double purchasePrice = 1000000,
|
||||||
|
String status = 'AVAILABLE',
|
||||||
|
int? currentCompanyId,
|
||||||
|
int? currentBranchId,
|
||||||
|
int? warehouseLocationId,
|
||||||
|
String? remark,
|
||||||
|
String? companyName,
|
||||||
|
String? branchName,
|
||||||
|
String? warehouseName,
|
||||||
|
}) {
|
||||||
|
return EquipmentResponse(
|
||||||
|
id: id,
|
||||||
|
equipmentNumber: equipmentNumber,
|
||||||
|
category1: category1 ?? '전자기기',
|
||||||
|
category2: category2,
|
||||||
|
category3: category3,
|
||||||
|
manufacturer: manufacturer,
|
||||||
|
modelName: modelName ?? 'TEST-MODEL-001',
|
||||||
|
serialNumber: serialNumber ?? 'SN123456789',
|
||||||
|
barcode: barcode,
|
||||||
|
purchaseDate: purchaseDate ?? DateTime.now().subtract(const Duration(days: 30)),
|
||||||
|
purchasePrice: purchasePrice,
|
||||||
|
status: status,
|
||||||
|
currentCompanyId: currentCompanyId,
|
||||||
|
currentBranchId: currentBranchId,
|
||||||
|
warehouseLocationId: warehouseLocationId,
|
||||||
|
remark: remark,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
companyName: companyName,
|
||||||
|
branchName: branchName,
|
||||||
|
warehouseName: warehouseName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 사용자 DTO 생성
|
||||||
|
static UserDto createMockUserDto({
|
||||||
|
int id = 1,
|
||||||
|
String username = 'testuser',
|
||||||
|
String email = 'test@example.com',
|
||||||
|
String name = '테스트 사용자',
|
||||||
|
String? phone,
|
||||||
|
String role = 'staff',
|
||||||
|
bool isActive = true,
|
||||||
|
int? companyId,
|
||||||
|
int? branchId,
|
||||||
|
}) {
|
||||||
|
return UserDto(
|
||||||
|
id: id,
|
||||||
|
username: username,
|
||||||
|
email: email,
|
||||||
|
name: name,
|
||||||
|
phone: phone ?? '010-1234-5678',
|
||||||
|
role: role,
|
||||||
|
isActive: isActive,
|
||||||
|
companyId: companyId ?? 1,
|
||||||
|
branchId: branchId ?? 1,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 라이선스 생성
|
||||||
|
static LicenseDto createMockLicense({
|
||||||
|
int id = 1,
|
||||||
|
String licenseKey = 'TEST-LICENSE-KEY-12345',
|
||||||
|
String? productName,
|
||||||
|
String? vendor,
|
||||||
|
String? licenseType,
|
||||||
|
int? userCount,
|
||||||
|
DateTime? purchaseDate,
|
||||||
|
DateTime? expiryDate,
|
||||||
|
double? purchasePrice,
|
||||||
|
int? companyId,
|
||||||
|
int? branchId,
|
||||||
|
int? assignedUserId,
|
||||||
|
String? remark,
|
||||||
|
bool isActive = true,
|
||||||
|
}) {
|
||||||
|
return LicenseDto(
|
||||||
|
id: id,
|
||||||
|
licenseKey: licenseKey,
|
||||||
|
productName: productName ?? '테스트 라이선스',
|
||||||
|
vendor: vendor ?? '테스트 벤더',
|
||||||
|
licenseType: licenseType ?? 'SOFTWARE',
|
||||||
|
userCount: userCount ?? 10,
|
||||||
|
purchaseDate: purchaseDate ?? DateTime.now().subtract(const Duration(days: 365)),
|
||||||
|
expiryDate: expiryDate ?? DateTime.now().add(const Duration(days: 365)),
|
||||||
|
purchasePrice: purchasePrice ?? 500000,
|
||||||
|
companyId: companyId,
|
||||||
|
branchId: branchId,
|
||||||
|
assignedUserId: assignedUserId,
|
||||||
|
remark: remark,
|
||||||
|
isActive: isActive,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 창고 위치 생성 (WarehouseDto가 없으므로 Map 반환)
|
||||||
|
static Map<String, dynamic> createMockWarehouse({
|
||||||
|
int id = 1,
|
||||||
|
String code = 'WH001',
|
||||||
|
String name = '메인 창고',
|
||||||
|
String type = 'WAREHOUSE',
|
||||||
|
String? location,
|
||||||
|
int? capacity,
|
||||||
|
int? currentOccupancy,
|
||||||
|
String? manager,
|
||||||
|
String? contactNumber,
|
||||||
|
bool isActive = true,
|
||||||
|
String? notes,
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'code': code,
|
||||||
|
'name': name,
|
||||||
|
'type': type,
|
||||||
|
'location': location ?? '서울시 강서구 물류단지',
|
||||||
|
'capacity': capacity ?? 1000,
|
||||||
|
'currentOccupancy': currentOccupancy ?? 250,
|
||||||
|
'manager': manager ?? '김창고',
|
||||||
|
'contactNumber': contactNumber ?? '02-9876-5432',
|
||||||
|
'isActive': isActive,
|
||||||
|
'notes': notes,
|
||||||
|
'createdAt': DateTime.now().toIso8601String(),
|
||||||
|
'updatedAt': DateTime.now().toIso8601String(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 페이지네이션 응답 생성
|
||||||
|
static PaginatedResponse<T> createMockPaginatedResponse<T>({
|
||||||
|
required List<T> data,
|
||||||
|
int page = 1,
|
||||||
|
int size = 20,
|
||||||
|
int? totalElements,
|
||||||
|
int? totalPages,
|
||||||
|
}) {
|
||||||
|
final total = totalElements ?? data.length;
|
||||||
|
final pages = totalPages ?? ((total + size - 1) ~/ size);
|
||||||
|
|
||||||
|
return PaginatedResponse<T>(
|
||||||
|
items: data,
|
||||||
|
page: page,
|
||||||
|
size: size,
|
||||||
|
totalElements: total,
|
||||||
|
totalPages: pages,
|
||||||
|
first: page == 1,
|
||||||
|
last: page >= pages,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 장비 목록 생성
|
||||||
|
static List<EquipmentResponse> createMockEquipmentList({int count = 10}) {
|
||||||
|
return List.generate(
|
||||||
|
count,
|
||||||
|
(index) => createMockEquipment(
|
||||||
|
id: index + 1,
|
||||||
|
equipmentNumber: 'EQ${(index + 1).toString().padLeft(3, '0')}',
|
||||||
|
category1: '전자기기',
|
||||||
|
modelName: '테스트 장비 ${index + 1}',
|
||||||
|
serialNumber: 'SN${DateTime.now().millisecondsSinceEpoch}${index}',
|
||||||
|
status: index % 3 == 0 ? 'IN_USE' : 'AVAILABLE',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock UnifiedEquipment 생성
|
||||||
|
static UnifiedEquipment createMockUnifiedEquipment({
|
||||||
|
int id = 1,
|
||||||
|
int equipmentId = 1,
|
||||||
|
String manufacturer = '삼성전자',
|
||||||
|
String name = '노트북',
|
||||||
|
String category = 'IT장비',
|
||||||
|
String subCategory = '컴퓨터',
|
||||||
|
String subSubCategory = '노트북',
|
||||||
|
String? serialNumber,
|
||||||
|
int quantity = 1,
|
||||||
|
String status = 'I', // I: 입고, O: 출고, R: 대여
|
||||||
|
DateTime? date,
|
||||||
|
String? notes,
|
||||||
|
}) {
|
||||||
|
final equipment = Equipment(
|
||||||
|
id: equipmentId,
|
||||||
|
manufacturer: manufacturer,
|
||||||
|
name: name,
|
||||||
|
category: category,
|
||||||
|
subCategory: subCategory,
|
||||||
|
subSubCategory: subSubCategory,
|
||||||
|
serialNumber: serialNumber ?? 'SN${DateTime.now().millisecondsSinceEpoch}',
|
||||||
|
quantity: quantity,
|
||||||
|
inDate: date ?? DateTime.now(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return UnifiedEquipment(
|
||||||
|
id: id,
|
||||||
|
equipment: equipment,
|
||||||
|
date: date ?? DateTime.now(),
|
||||||
|
status: status,
|
||||||
|
notes: notes,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock UnifiedEquipment 목록 생성
|
||||||
|
static List<UnifiedEquipment> createMockUnifiedEquipmentList({int count = 10}) {
|
||||||
|
return List.generate(
|
||||||
|
count,
|
||||||
|
(index) => createMockUnifiedEquipment(
|
||||||
|
id: index + 1,
|
||||||
|
equipmentId: index + 1,
|
||||||
|
name: '테스트 장비 ${index + 1}',
|
||||||
|
status: index % 3 == 0 ? 'O' : 'I', // 일부는 출고, 대부분은 입고 상태
|
||||||
|
serialNumber: 'SN${DateTime.now().millisecondsSinceEpoch}${index}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 회사 목록 생성
|
||||||
|
static List<Company> createMockCompanyList({int count = 10}) {
|
||||||
|
return List.generate(
|
||||||
|
count,
|
||||||
|
(index) => createMockCompany(
|
||||||
|
id: index + 1,
|
||||||
|
name: '테스트 회사 ${index + 1}',
|
||||||
|
contactName: '담당자 ${index + 1}',
|
||||||
|
contactPosition: '대리',
|
||||||
|
contactPhone: '02-${1000 + index}-${5678 + index}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 사용자 목록 생성
|
||||||
|
static List<UserDto> createMockUserList({int count = 10}) {
|
||||||
|
return List.generate(
|
||||||
|
count,
|
||||||
|
(index) => createMockUserDto(
|
||||||
|
id: index + 1,
|
||||||
|
username: 'user${index + 1}',
|
||||||
|
email: 'user${index + 1}@test.com',
|
||||||
|
name: '사용자 ${index + 1}',
|
||||||
|
phone: '010-${1000 + index}-${1000 + index}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 라이선스 목록 생성
|
||||||
|
static List<LicenseDto> createMockLicenseList({int count = 10}) {
|
||||||
|
return List.generate(
|
||||||
|
count,
|
||||||
|
(index) => createMockLicense(
|
||||||
|
id: index + 1,
|
||||||
|
productName: '라이선스 ${index + 1}',
|
||||||
|
licenseKey: 'KEY-${DateTime.now().millisecondsSinceEpoch}-${index}',
|
||||||
|
licenseType: index % 2 == 0 ? 'SOFTWARE' : 'HARDWARE',
|
||||||
|
isActive: index % 4 != 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 창고 목록 생성
|
||||||
|
static List<dynamic> createMockWarehouseList({int count = 5}) {
|
||||||
|
return List.generate(
|
||||||
|
count,
|
||||||
|
(index) => createMockWarehouse(
|
||||||
|
id: index + 1,
|
||||||
|
code: 'WH${(index + 1).toString().padLeft(3, '0')}',
|
||||||
|
name: '창고 ${index + 1}',
|
||||||
|
type: index % 2 == 0 ? 'WAREHOUSE' : 'STORAGE',
|
||||||
|
currentOccupancy: (index + 1) * 50,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock Equipment 모델 생성
|
||||||
|
static Equipment createMockEquipmentModel({
|
||||||
|
int? id,
|
||||||
|
String manufacturer = '삼성전자',
|
||||||
|
String name = '노트북',
|
||||||
|
String category = 'IT장비',
|
||||||
|
String subCategory = '컴퓨터',
|
||||||
|
String subSubCategory = '노트북',
|
||||||
|
String? serialNumber,
|
||||||
|
String? barcode,
|
||||||
|
int quantity = 1,
|
||||||
|
DateTime? inDate,
|
||||||
|
String? remark,
|
||||||
|
String? warrantyLicense,
|
||||||
|
DateTime? warrantyStartDate,
|
||||||
|
DateTime? warrantyEndDate,
|
||||||
|
}) {
|
||||||
|
return Equipment(
|
||||||
|
id: id ?? 1,
|
||||||
|
manufacturer: manufacturer,
|
||||||
|
name: name,
|
||||||
|
category: category,
|
||||||
|
subCategory: subCategory,
|
||||||
|
subSubCategory: subSubCategory,
|
||||||
|
serialNumber: serialNumber ?? 'SN${DateTime.now().millisecondsSinceEpoch}',
|
||||||
|
barcode: barcode,
|
||||||
|
quantity: quantity,
|
||||||
|
inDate: inDate ?? DateTime.now(),
|
||||||
|
remark: remark,
|
||||||
|
warrantyLicense: warrantyLicense,
|
||||||
|
warrantyStartDate: warrantyStartDate,
|
||||||
|
warrantyEndDate: warrantyEndDate,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock Equipment 모델 목록 생성
|
||||||
|
static List<Equipment> createMockEquipmentModelList({int count = 10}) {
|
||||||
|
return List.generate(
|
||||||
|
count,
|
||||||
|
(index) => createMockEquipmentModel(
|
||||||
|
id: index + 1,
|
||||||
|
name: '테스트 장비 ${index + 1}',
|
||||||
|
category: index % 2 == 0 ? 'IT장비' : '사무용품',
|
||||||
|
serialNumber: 'SN${DateTime.now().millisecondsSinceEpoch}${index}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock User 모델 생성 (UserDto가 아닌 User 모델)
|
||||||
|
static User createMockUserModel({
|
||||||
|
int? id,
|
||||||
|
int companyId = 1,
|
||||||
|
int? branchId,
|
||||||
|
String name = '테스트 사용자',
|
||||||
|
String role = 'M',
|
||||||
|
String? position,
|
||||||
|
String? email,
|
||||||
|
List<Map<String, String>>? phoneNumbers,
|
||||||
|
String? username,
|
||||||
|
bool isActive = true,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
}) {
|
||||||
|
return User(
|
||||||
|
id: id ?? 1,
|
||||||
|
companyId: companyId,
|
||||||
|
branchId: branchId ?? 1,
|
||||||
|
name: name,
|
||||||
|
role: role,
|
||||||
|
position: position ?? '대리',
|
||||||
|
email: email ?? 'user@test.com',
|
||||||
|
phoneNumbers: phoneNumbers ?? [{'type': '기본', 'number': '010-1234-5678'}],
|
||||||
|
username: username ?? 'testuser',
|
||||||
|
isActive: isActive,
|
||||||
|
createdAt: createdAt ?? DateTime.now(),
|
||||||
|
updatedAt: updatedAt ?? DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock User 모델 목록 생성
|
||||||
|
static List<User> createMockUserModelList({int count = 10}) {
|
||||||
|
return List.generate(
|
||||||
|
count,
|
||||||
|
(index) => createMockUserModel(
|
||||||
|
id: index + 1,
|
||||||
|
name: '사용자 ${index + 1}',
|
||||||
|
username: 'user${index + 1}',
|
||||||
|
email: 'user${index + 1}@test.com',
|
||||||
|
role: index % 3 == 0 ? 'S' : 'M',
|
||||||
|
isActive: index % 5 != 0,
|
||||||
|
phoneNumbers: [{'type': '기본', 'number': '010-${1000 + index}-${1000 + index}'}],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 장비 입출고 응답 생성
|
||||||
|
static EquipmentIoResponse createMockEquipmentIoResponse({
|
||||||
|
bool success = true,
|
||||||
|
String? message,
|
||||||
|
int transactionId = 1,
|
||||||
|
int equipmentId = 1,
|
||||||
|
int quantity = 1,
|
||||||
|
String transactionType = 'IN',
|
||||||
|
DateTime? transactionDate,
|
||||||
|
}) {
|
||||||
|
return EquipmentIoResponse(
|
||||||
|
success: success,
|
||||||
|
message: message ?? (success ? '장비 처리가 완료되었습니다.' : '장비 처리에 실패했습니다.'),
|
||||||
|
transactionId: transactionId,
|
||||||
|
equipmentId: equipmentId,
|
||||||
|
quantity: quantity,
|
||||||
|
transactionType: transactionType,
|
||||||
|
transactionDate: transactionDate ?? DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock 창고 용량 정보 생성
|
||||||
|
static WarehouseCapacityInfo createMockWarehouseCapacityInfo({
|
||||||
|
int warehouseId = 1,
|
||||||
|
int totalCapacity = 1000,
|
||||||
|
int usedCapacity = 250,
|
||||||
|
int? availableCapacity,
|
||||||
|
double? usagePercentage,
|
||||||
|
int equipmentCount = 50,
|
||||||
|
}) {
|
||||||
|
final available = availableCapacity ?? (totalCapacity - usedCapacity);
|
||||||
|
final percentage = usagePercentage ?? (usedCapacity / totalCapacity * 100);
|
||||||
|
|
||||||
|
return WarehouseCapacityInfo(
|
||||||
|
warehouseId: warehouseId,
|
||||||
|
totalCapacity: totalCapacity,
|
||||||
|
usedCapacity: usedCapacity,
|
||||||
|
availableCapacity: available,
|
||||||
|
usagePercentage: percentage,
|
||||||
|
equipmentCount: equipmentCount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock WarehouseLocation 생성
|
||||||
|
static WarehouseLocation createMockWarehouseLocation({
|
||||||
|
int id = 1,
|
||||||
|
String name = '메인 창고',
|
||||||
|
String? addressStr,
|
||||||
|
String? remark,
|
||||||
|
}) {
|
||||||
|
return WarehouseLocation(
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
address: Address(
|
||||||
|
zipCode: '12345',
|
||||||
|
region: addressStr ?? '서울시 강서구 물류단지',
|
||||||
|
detailAddress: '창고동 A동',
|
||||||
|
),
|
||||||
|
remark: remark,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock License 모델 생성
|
||||||
|
static License createMockLicenseModel({
|
||||||
|
int? id,
|
||||||
|
String licenseKey = 'TEST-LICENSE-KEY',
|
||||||
|
String productName = '테스트 라이선스',
|
||||||
|
String? vendor,
|
||||||
|
String? licenseType,
|
||||||
|
int? userCount,
|
||||||
|
DateTime? purchaseDate,
|
||||||
|
DateTime? expiryDate,
|
||||||
|
double? purchasePrice,
|
||||||
|
int? companyId,
|
||||||
|
int? branchId,
|
||||||
|
int? assignedUserId,
|
||||||
|
String? remark,
|
||||||
|
bool isActive = true,
|
||||||
|
}) {
|
||||||
|
return License(
|
||||||
|
id: id ?? 1,
|
||||||
|
licenseKey: licenseKey,
|
||||||
|
productName: productName,
|
||||||
|
vendor: vendor ?? '테스트 벤더',
|
||||||
|
licenseType: licenseType ?? 'SOFTWARE',
|
||||||
|
userCount: userCount ?? 10,
|
||||||
|
purchaseDate: purchaseDate ?? DateTime.now().subtract(const Duration(days: 365)),
|
||||||
|
expiryDate: expiryDate ?? DateTime.now().add(const Duration(days: 365)),
|
||||||
|
purchasePrice: purchasePrice ?? 500000,
|
||||||
|
companyId: companyId ?? 1,
|
||||||
|
branchId: branchId,
|
||||||
|
assignedUserId: assignedUserId,
|
||||||
|
remark: remark,
|
||||||
|
isActive: isActive,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock License 모델 목록 생성
|
||||||
|
static List<License> createMockLicenseModelList({int count = 10}) {
|
||||||
|
return List.generate(
|
||||||
|
count,
|
||||||
|
(index) => createMockLicenseModel(
|
||||||
|
id: index + 1,
|
||||||
|
productName: '라이선스 ${index + 1}',
|
||||||
|
licenseKey: 'KEY-${DateTime.now().millisecondsSinceEpoch}-${index}',
|
||||||
|
licenseType: index % 2 == 0 ? 'SOFTWARE' : 'HARDWARE',
|
||||||
|
isActive: index % 4 != 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mock WarehouseLocation 목록 생성
|
||||||
|
static List<WarehouseLocation> createMockWarehouseLocationList({int count = 5}) {
|
||||||
|
return List.generate(
|
||||||
|
count,
|
||||||
|
(index) => createMockWarehouseLocation(
|
||||||
|
id: index + 1,
|
||||||
|
name: '창고 ${index + 1}',
|
||||||
|
addressStr: '서울시 ${index % 3 == 0 ? "강서구" : index % 3 == 1 ? "강남구" : "송파구"} 물류단지 ${index + 1}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
525
test/helpers/mock_services.dart
Normal file
525
test/helpers/mock_services.dart
Normal file
@@ -0,0 +1,525 @@
|
|||||||
|
import 'package:mockito/annotations.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:superport/services/auth_service.dart';
|
||||||
|
import 'package:superport/services/company_service.dart';
|
||||||
|
import 'package:superport/services/equipment_service.dart';
|
||||||
|
import 'package:superport/services/user_service.dart';
|
||||||
|
import 'package:superport/services/license_service.dart';
|
||||||
|
import 'package:superport/services/warehouse_service.dart';
|
||||||
|
import 'package:superport/services/dashboard_service.dart';
|
||||||
|
import 'package:superport/services/mock_data_service.dart';
|
||||||
|
import 'package:superport/core/errors/failures.dart';
|
||||||
|
|
||||||
|
import 'mock_data_helpers.dart';
|
||||||
|
import 'mock_services.mocks.dart';
|
||||||
|
|
||||||
|
// Mockito 어노테이션으로 Mock 클래스 생성
|
||||||
|
@GenerateMocks([
|
||||||
|
AuthService,
|
||||||
|
CompanyService,
|
||||||
|
EquipmentService,
|
||||||
|
UserService,
|
||||||
|
LicenseService,
|
||||||
|
WarehouseService,
|
||||||
|
DashboardService,
|
||||||
|
MockDataService,
|
||||||
|
])
|
||||||
|
void main() {}
|
||||||
|
|
||||||
|
/// Mock 서비스 설정 헬퍼
|
||||||
|
class MockServiceHelpers {
|
||||||
|
/// AuthService Mock 설정
|
||||||
|
static void setupAuthServiceMock(
|
||||||
|
MockAuthService mockAuthService, {
|
||||||
|
bool isLoggedIn = false,
|
||||||
|
bool loginSuccess = true,
|
||||||
|
bool logoutSuccess = true,
|
||||||
|
}) {
|
||||||
|
// isLoggedIn
|
||||||
|
when(mockAuthService.isLoggedIn())
|
||||||
|
.thenAnswer((_) async => isLoggedIn);
|
||||||
|
|
||||||
|
// login
|
||||||
|
if (loginSuccess) {
|
||||||
|
when(mockAuthService.login(any))
|
||||||
|
.thenAnswer((_) async => Right(MockDataHelpers.createMockLoginResponse()));
|
||||||
|
} else {
|
||||||
|
when(mockAuthService.login(any))
|
||||||
|
.thenAnswer((_) async => Left(AuthenticationFailure(
|
||||||
|
message: '이메일 또는 비밀번호가 올바르지 않습니다.',
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// logout
|
||||||
|
if (logoutSuccess) {
|
||||||
|
when(mockAuthService.logout())
|
||||||
|
.thenAnswer((_) async => const Right(null));
|
||||||
|
} else {
|
||||||
|
when(mockAuthService.logout())
|
||||||
|
.thenAnswer((_) async => Left(ServerFailure(
|
||||||
|
message: '로그아웃 중 오류가 발생했습니다.',
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCurrentUser
|
||||||
|
when(mockAuthService.getCurrentUser())
|
||||||
|
.thenAnswer((_) async => isLoggedIn ? MockDataHelpers.createMockUser() : null);
|
||||||
|
|
||||||
|
// getAccessToken
|
||||||
|
when(mockAuthService.getAccessToken())
|
||||||
|
.thenAnswer((_) async => isLoggedIn ? 'test_access_token' : null);
|
||||||
|
|
||||||
|
// authStateChanges
|
||||||
|
when(mockAuthService.authStateChanges)
|
||||||
|
.thenAnswer((_) => Stream.value(isLoggedIn));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CompanyService Mock 설정
|
||||||
|
static void setupCompanyServiceMock(
|
||||||
|
MockCompanyService mockCompanyService, {
|
||||||
|
bool getCompaniesSuccess = true,
|
||||||
|
bool createCompanySuccess = true,
|
||||||
|
bool updateCompanySuccess = true,
|
||||||
|
bool deleteCompanySuccess = true,
|
||||||
|
int companyCount = 10,
|
||||||
|
}) {
|
||||||
|
// getCompanies
|
||||||
|
if (getCompaniesSuccess) {
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async =>
|
||||||
|
MockDataHelpers.createMockCompanyList(count: companyCount),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenThrow(
|
||||||
|
ServerFailure(message: '회사 목록을 불러오는 중 오류가 발생했습니다.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// createCompany
|
||||||
|
if (createCompanySuccess) {
|
||||||
|
when(mockCompanyService.createCompany(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockCompany());
|
||||||
|
} else {
|
||||||
|
when(mockCompanyService.createCompany(any))
|
||||||
|
.thenThrow(ServerFailure(
|
||||||
|
message: '회사 등록 중 오류가 발생했습니다.',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateCompany
|
||||||
|
if (updateCompanySuccess) {
|
||||||
|
when(mockCompanyService.updateCompany(any, any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockCompany());
|
||||||
|
} else {
|
||||||
|
when(mockCompanyService.updateCompany(any, any))
|
||||||
|
.thenThrow(ServerFailure(
|
||||||
|
message: '회사 정보 수정 중 오류가 발생했습니다.',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteCompany
|
||||||
|
if (deleteCompanySuccess) {
|
||||||
|
when(mockCompanyService.deleteCompany(any))
|
||||||
|
.thenAnswer((_) async {});
|
||||||
|
} else {
|
||||||
|
when(mockCompanyService.deleteCompany(any))
|
||||||
|
.thenThrow(ServerFailure(
|
||||||
|
message: '회사 삭제 중 오류가 발생했습니다.',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCompanyDetail
|
||||||
|
when(mockCompanyService.getCompanyDetail(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockCompany());
|
||||||
|
|
||||||
|
// checkDuplicateCompany
|
||||||
|
when(mockCompanyService.checkDuplicateCompany(any))
|
||||||
|
.thenAnswer((_) async => false);
|
||||||
|
|
||||||
|
// getCompanyNames
|
||||||
|
when(mockCompanyService.getCompanyNames())
|
||||||
|
.thenAnswer((_) async => [{'id': 1, 'name': '테스트 회사 1'}, {'id': 2, 'name': '테스트 회사 2'}, {'id': 3, 'name': '테스트 회사 3'}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// EquipmentService Mock 설정
|
||||||
|
static void setupEquipmentServiceMock(
|
||||||
|
MockEquipmentService mockEquipmentService, {
|
||||||
|
bool getEquipmentsSuccess = true,
|
||||||
|
bool createEquipmentSuccess = true,
|
||||||
|
bool equipmentInSuccess = true,
|
||||||
|
bool equipmentOutSuccess = true,
|
||||||
|
int equipmentCount = 10,
|
||||||
|
}) {
|
||||||
|
// getEquipments
|
||||||
|
if (getEquipmentsSuccess) {
|
||||||
|
when(mockEquipmentService.getEquipments(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
status: anyNamed('status'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
warehouseLocationId: anyNamed('warehouseLocationId'),
|
||||||
|
)).thenAnswer((_) async =>
|
||||||
|
MockDataHelpers.createMockEquipmentModelList(count: equipmentCount),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
when(mockEquipmentService.getEquipments(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
status: anyNamed('status'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
warehouseLocationId: anyNamed('warehouseLocationId'),
|
||||||
|
)).thenThrow(ServerFailure(
|
||||||
|
message: '장비 목록을 불러오는 중 오류가 발생했습니다.',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// createEquipment
|
||||||
|
if (createEquipmentSuccess) {
|
||||||
|
when(mockEquipmentService.createEquipment(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockEquipmentModel());
|
||||||
|
} else {
|
||||||
|
when(mockEquipmentService.createEquipment(any))
|
||||||
|
.thenThrow(ServerFailure(
|
||||||
|
message: '장비 등록 중 오류가 발생했습니다.',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// equipmentIn
|
||||||
|
if (equipmentInSuccess) {
|
||||||
|
when(mockEquipmentService.equipmentIn(
|
||||||
|
equipmentId: anyNamed('equipmentId'),
|
||||||
|
quantity: anyNamed('quantity'),
|
||||||
|
warehouseLocationId: anyNamed('warehouseLocationId'),
|
||||||
|
notes: anyNamed('notes'),
|
||||||
|
)).thenAnswer((_) async => MockDataHelpers.createMockEquipmentIoResponse());
|
||||||
|
} else {
|
||||||
|
when(mockEquipmentService.equipmentIn(
|
||||||
|
equipmentId: anyNamed('equipmentId'),
|
||||||
|
quantity: anyNamed('quantity'),
|
||||||
|
warehouseLocationId: anyNamed('warehouseLocationId'),
|
||||||
|
notes: anyNamed('notes'),
|
||||||
|
)).thenThrow(ServerFailure(
|
||||||
|
message: '장비 입고 처리 중 오류가 발생했습니다.',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// equipmentOut
|
||||||
|
if (equipmentOutSuccess) {
|
||||||
|
when(mockEquipmentService.equipmentOut(
|
||||||
|
equipmentId: anyNamed('equipmentId'),
|
||||||
|
quantity: anyNamed('quantity'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
branchId: anyNamed('branchId'),
|
||||||
|
notes: anyNamed('notes'),
|
||||||
|
)).thenAnswer((_) async => MockDataHelpers.createMockEquipmentIoResponse());
|
||||||
|
} else {
|
||||||
|
when(mockEquipmentService.equipmentOut(
|
||||||
|
equipmentId: anyNamed('equipmentId'),
|
||||||
|
quantity: anyNamed('quantity'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
branchId: anyNamed('branchId'),
|
||||||
|
notes: anyNamed('notes'),
|
||||||
|
)).thenThrow(ServerFailure(
|
||||||
|
message: '장비 출고 처리 중 오류가 발생했습니다.',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEquipmentDetail
|
||||||
|
when(mockEquipmentService.getEquipmentDetail(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockEquipmentModel());
|
||||||
|
|
||||||
|
// getEquipment (alias for getEquipmentDetail)
|
||||||
|
when(mockEquipmentService.getEquipment(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockEquipmentModel());
|
||||||
|
|
||||||
|
// getEquipmentHistory
|
||||||
|
when(mockEquipmentService.getEquipmentHistory(any, page: anyNamed('page'), perPage: anyNamed('perPage')))
|
||||||
|
.thenAnswer((_) async => []);
|
||||||
|
|
||||||
|
// Additional mock setups can be added here if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UserService Mock 설정
|
||||||
|
static void setupUserServiceMock(
|
||||||
|
MockUserService mockUserService, {
|
||||||
|
bool getUsersSuccess = true,
|
||||||
|
bool createUserSuccess = true,
|
||||||
|
bool updateUserSuccess = true,
|
||||||
|
bool deleteUserSuccess = true,
|
||||||
|
int userCount = 10,
|
||||||
|
}) {
|
||||||
|
// getUsers
|
||||||
|
if (getUsersSuccess) {
|
||||||
|
when(mockUserService.getUsers(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
role: anyNamed('role'),
|
||||||
|
)).thenAnswer((_) async =>
|
||||||
|
MockDataHelpers.createMockUserModelList(count: userCount),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
when(mockUserService.getUsers(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
role: anyNamed('role'),
|
||||||
|
)).thenThrow(Exception('사용자 목록 조회 실패'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// createUser
|
||||||
|
if (createUserSuccess) {
|
||||||
|
when(mockUserService.createUser(
|
||||||
|
username: anyNamed('username'),
|
||||||
|
email: anyNamed('email'),
|
||||||
|
password: anyNamed('password'),
|
||||||
|
name: anyNamed('name'),
|
||||||
|
role: anyNamed('role'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
branchId: anyNamed('branchId'),
|
||||||
|
phone: anyNamed('phone'),
|
||||||
|
position: anyNamed('position'),
|
||||||
|
)).thenAnswer((_) async => MockDataHelpers.createMockUserModel());
|
||||||
|
} else {
|
||||||
|
when(mockUserService.createUser(
|
||||||
|
username: anyNamed('username'),
|
||||||
|
email: anyNamed('email'),
|
||||||
|
password: anyNamed('password'),
|
||||||
|
name: anyNamed('name'),
|
||||||
|
role: anyNamed('role'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
branchId: anyNamed('branchId'),
|
||||||
|
phone: anyNamed('phone'),
|
||||||
|
position: anyNamed('position'),
|
||||||
|
)).thenThrow(Exception('사용자 생성 실패'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateUser
|
||||||
|
if (updateUserSuccess) {
|
||||||
|
when(mockUserService.updateUser(
|
||||||
|
any,
|
||||||
|
name: anyNamed('name'),
|
||||||
|
email: anyNamed('email'),
|
||||||
|
password: anyNamed('password'),
|
||||||
|
phone: anyNamed('phone'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
branchId: anyNamed('branchId'),
|
||||||
|
role: anyNamed('role'),
|
||||||
|
position: anyNamed('position'),
|
||||||
|
)).thenAnswer((_) async => MockDataHelpers.createMockUserModel());
|
||||||
|
} else {
|
||||||
|
when(mockUserService.updateUser(
|
||||||
|
any,
|
||||||
|
name: anyNamed('name'),
|
||||||
|
email: anyNamed('email'),
|
||||||
|
password: anyNamed('password'),
|
||||||
|
phone: anyNamed('phone'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
branchId: anyNamed('branchId'),
|
||||||
|
role: anyNamed('role'),
|
||||||
|
position: anyNamed('position'),
|
||||||
|
)).thenThrow(Exception('사용자 수정 실패'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteUser
|
||||||
|
if (deleteUserSuccess) {
|
||||||
|
when(mockUserService.deleteUser(any))
|
||||||
|
.thenAnswer((_) async {});
|
||||||
|
} else {
|
||||||
|
when(mockUserService.deleteUser(any))
|
||||||
|
.thenThrow(Exception('사용자 삭제 실패'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUser
|
||||||
|
when(mockUserService.getUser(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockUserModel());
|
||||||
|
|
||||||
|
// changePassword
|
||||||
|
when(mockUserService.changePassword(any, any, any))
|
||||||
|
.thenAnswer((_) async {});
|
||||||
|
|
||||||
|
// changeUserStatus
|
||||||
|
when(mockUserService.changeUserStatus(any, any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockUserModel());
|
||||||
|
|
||||||
|
// checkDuplicateUsername
|
||||||
|
when(mockUserService.checkDuplicateUsername(any))
|
||||||
|
.thenAnswer((_) async => false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// LicenseService Mock 설정
|
||||||
|
static void setupLicenseServiceMock(
|
||||||
|
MockLicenseService mockLicenseService, {
|
||||||
|
bool getLicensesSuccess = true,
|
||||||
|
bool createLicenseSuccess = true,
|
||||||
|
bool updateLicenseSuccess = true,
|
||||||
|
bool deleteLicenseSuccess = true,
|
||||||
|
int licenseCount = 10,
|
||||||
|
}) {
|
||||||
|
// getLicenses
|
||||||
|
if (getLicensesSuccess) {
|
||||||
|
when(mockLicenseService.getLicenses(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
assignedUserId: anyNamed('assignedUserId'),
|
||||||
|
licenseType: anyNamed('licenseType'),
|
||||||
|
)).thenAnswer((_) async =>
|
||||||
|
MockDataHelpers.createMockLicenseModelList(count: licenseCount),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
when(mockLicenseService.getLicenses(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
assignedUserId: anyNamed('assignedUserId'),
|
||||||
|
licenseType: anyNamed('licenseType'),
|
||||||
|
)).thenThrow(ServerFailure(
|
||||||
|
message: '라이선스 목록을 불러오는 데 실패했습니다',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// createLicense
|
||||||
|
if (createLicenseSuccess) {
|
||||||
|
when(mockLicenseService.createLicense(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockLicenseModel());
|
||||||
|
} else {
|
||||||
|
when(mockLicenseService.createLicense(any))
|
||||||
|
.thenThrow(ServerFailure(
|
||||||
|
message: '라이선스 생성에 실패했습니다',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateLicense
|
||||||
|
if (updateLicenseSuccess) {
|
||||||
|
when(mockLicenseService.updateLicense(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockLicenseModel());
|
||||||
|
} else {
|
||||||
|
when(mockLicenseService.updateLicense(any))
|
||||||
|
.thenThrow(ServerFailure(
|
||||||
|
message: '라이선스 수정에 실패했습니다',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteLicense
|
||||||
|
if (deleteLicenseSuccess) {
|
||||||
|
when(mockLicenseService.deleteLicense(any))
|
||||||
|
.thenAnswer((_) async {});
|
||||||
|
} else {
|
||||||
|
when(mockLicenseService.deleteLicense(any))
|
||||||
|
.thenThrow(ServerFailure(
|
||||||
|
message: '라이선스 삭제에 실패했습니다',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLicenseById
|
||||||
|
when(mockLicenseService.getLicenseById(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockLicenseModel());
|
||||||
|
|
||||||
|
// getExpiringLicenses
|
||||||
|
when(mockLicenseService.getExpiringLicenses(
|
||||||
|
days: anyNamed('days'),
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
)).thenAnswer((_) async => MockDataHelpers.createMockLicenseModelList(count: 3));
|
||||||
|
|
||||||
|
// assignLicense
|
||||||
|
when(mockLicenseService.assignLicense(any, any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockLicenseModel());
|
||||||
|
|
||||||
|
// unassignLicense
|
||||||
|
when(mockLicenseService.unassignLicense(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockLicenseModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// WarehouseService Mock 설정
|
||||||
|
static void setupWarehouseServiceMock(
|
||||||
|
MockWarehouseService mockWarehouseService, {
|
||||||
|
bool getWarehousesSuccess = true,
|
||||||
|
bool createWarehouseSuccess = true,
|
||||||
|
bool updateWarehouseSuccess = true,
|
||||||
|
bool deleteWarehouseSuccess = true,
|
||||||
|
int warehouseCount = 5,
|
||||||
|
}) {
|
||||||
|
// getWarehouses
|
||||||
|
if (getWarehousesSuccess) {
|
||||||
|
when(mockWarehouseService.getWarehouseLocations(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async =>
|
||||||
|
MockDataHelpers.createMockWarehouseLocationList(count: warehouseCount),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
when(mockWarehouseService.getWarehouseLocations(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenThrow(ServerFailure(
|
||||||
|
message: '창고 위치 목록을 불러오는 데 실패했습니다',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// createWarehouse
|
||||||
|
if (createWarehouseSuccess) {
|
||||||
|
when(mockWarehouseService.createWarehouseLocation(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockWarehouseLocation());
|
||||||
|
} else {
|
||||||
|
when(mockWarehouseService.createWarehouseLocation(any))
|
||||||
|
.thenThrow(ServerFailure(
|
||||||
|
message: '창고 위치 생성에 실패했습니다',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateWarehouse
|
||||||
|
if (updateWarehouseSuccess) {
|
||||||
|
when(mockWarehouseService.updateWarehouseLocation(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockWarehouseLocation());
|
||||||
|
} else {
|
||||||
|
when(mockWarehouseService.updateWarehouseLocation(any))
|
||||||
|
.thenThrow(ServerFailure(
|
||||||
|
message: '창고 위치 수정에 실패했습니다',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteWarehouse
|
||||||
|
if (deleteWarehouseSuccess) {
|
||||||
|
when(mockWarehouseService.deleteWarehouseLocation(any))
|
||||||
|
.thenAnswer((_) async {});
|
||||||
|
} else {
|
||||||
|
when(mockWarehouseService.deleteWarehouseLocation(any))
|
||||||
|
.thenThrow(ServerFailure(
|
||||||
|
message: '창고 위치 삭제에 실패했습니다',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// getWarehouseLocationById
|
||||||
|
when(mockWarehouseService.getWarehouseLocationById(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockWarehouseLocation());
|
||||||
|
|
||||||
|
// getWarehouseEquipment
|
||||||
|
when(mockWarehouseService.getWarehouseEquipment(
|
||||||
|
any,
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
)).thenAnswer((_) async => []);
|
||||||
|
|
||||||
|
// getWarehouseCapacity
|
||||||
|
when(mockWarehouseService.getWarehouseCapacity(any))
|
||||||
|
.thenAnswer((_) async => MockDataHelpers.createMockWarehouseCapacityInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
1896
test/helpers/mock_services.mocks.dart
Normal file
1896
test/helpers/mock_services.mocks.dart
Normal file
File diff suppressed because it is too large
Load Diff
174
test/helpers/simple_mock_services.dart
Normal file
174
test/helpers/simple_mock_services.dart
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import 'package:mockito/annotations.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:superport/services/auth_service.dart';
|
||||||
|
import 'package:superport/services/company_service.dart';
|
||||||
|
import 'package:superport/services/equipment_service.dart';
|
||||||
|
import 'package:superport/services/mock_data_service.dart';
|
||||||
|
import 'package:superport/services/user_service.dart';
|
||||||
|
import 'package:superport/data/models/auth/auth_user.dart';
|
||||||
|
import 'package:superport/models/user_model.dart';
|
||||||
|
|
||||||
|
import 'mock_data_helpers.dart';
|
||||||
|
import 'simple_mock_services.mocks.dart';
|
||||||
|
|
||||||
|
// Mockito 어노테이션으로 Mock 클래스 생성
|
||||||
|
@GenerateMocks([
|
||||||
|
AuthService,
|
||||||
|
CompanyService,
|
||||||
|
MockDataService,
|
||||||
|
EquipmentService,
|
||||||
|
UserService,
|
||||||
|
])
|
||||||
|
void main() {}
|
||||||
|
|
||||||
|
/// 간단한 Mock 서비스 설정 헬퍼
|
||||||
|
class SimpleMockServiceHelpers {
|
||||||
|
/// AuthService Mock 설정
|
||||||
|
static void setupAuthServiceMock(
|
||||||
|
MockAuthService mockAuthService, {
|
||||||
|
bool isLoggedIn = false,
|
||||||
|
}) {
|
||||||
|
// isLoggedIn
|
||||||
|
when(mockAuthService.isLoggedIn())
|
||||||
|
.thenAnswer((_) async => isLoggedIn);
|
||||||
|
|
||||||
|
// getCurrentUser
|
||||||
|
when(mockAuthService.getCurrentUser())
|
||||||
|
.thenAnswer((_) async => isLoggedIn ? AuthUser(
|
||||||
|
id: 1,
|
||||||
|
username: 'test_user',
|
||||||
|
name: '테스트 사용자',
|
||||||
|
email: 'test@example.com',
|
||||||
|
role: 'admin',
|
||||||
|
) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CompanyService Mock 설정
|
||||||
|
static void setupCompanyServiceMock(
|
||||||
|
MockCompanyService mockCompanyService, {
|
||||||
|
bool getCompaniesSuccess = true,
|
||||||
|
bool deleteCompanySuccess = true,
|
||||||
|
int companyCount = 10,
|
||||||
|
}) {
|
||||||
|
// getCompanies
|
||||||
|
if (getCompaniesSuccess) {
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async =>
|
||||||
|
MockDataHelpers.createMockCompanyList(count: companyCount),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenThrow(
|
||||||
|
Exception('회사 목록을 불러오는 중 오류가 발생했습니다.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteCompany
|
||||||
|
if (deleteCompanySuccess) {
|
||||||
|
when(mockCompanyService.deleteCompany(any))
|
||||||
|
.thenAnswer((_) async {});
|
||||||
|
} else {
|
||||||
|
when(mockCompanyService.deleteCompany(any))
|
||||||
|
.thenThrow(
|
||||||
|
Exception('회사 삭제 중 오류가 발생했습니다.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MockDataService Mock 설정
|
||||||
|
static void setupMockDataServiceMock(
|
||||||
|
MockMockDataService mockDataService, {
|
||||||
|
int companyCount = 10,
|
||||||
|
int userCount = 10,
|
||||||
|
}) {
|
||||||
|
when(mockDataService.getAllCompanies()).thenReturn(
|
||||||
|
MockDataHelpers.createMockCompanyList(count: companyCount)
|
||||||
|
);
|
||||||
|
|
||||||
|
when(mockDataService.deleteCompany(any)).thenReturn(null);
|
||||||
|
|
||||||
|
when(mockDataService.getAllUsers()).thenReturn(
|
||||||
|
MockDataHelpers.createMockUserModelList(count: userCount)
|
||||||
|
);
|
||||||
|
|
||||||
|
when(mockDataService.deleteUser(any)).thenReturn(null);
|
||||||
|
|
||||||
|
when(mockDataService.getCompanyById(any)).thenAnswer((invocation) {
|
||||||
|
final id = invocation.positionalArguments[0] as int;
|
||||||
|
final companies = MockDataHelpers.createMockCompanyList(count: companyCount);
|
||||||
|
return companies.firstWhere(
|
||||||
|
(c) => c.id == id,
|
||||||
|
orElse: () => MockDataHelpers.createMockCompany(id: id),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UserService Mock 설정
|
||||||
|
static void setupUserServiceMock(
|
||||||
|
MockUserService mockUserService, {
|
||||||
|
bool getUsersSuccess = true,
|
||||||
|
bool deleteUserSuccess = true,
|
||||||
|
bool changeUserStatusSuccess = true,
|
||||||
|
int userCount = 10,
|
||||||
|
}) {
|
||||||
|
// getUsers
|
||||||
|
if (getUsersSuccess) {
|
||||||
|
when(mockUserService.getUsers(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
role: anyNamed('role'),
|
||||||
|
)).thenAnswer((_) async =>
|
||||||
|
MockDataHelpers.createMockUserModelList(count: userCount),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
when(mockUserService.getUsers(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
role: anyNamed('role'),
|
||||||
|
)).thenThrow(
|
||||||
|
Exception('사용자 목록을 불러오는 중 오류가 발생했습니다.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteUser
|
||||||
|
if (deleteUserSuccess) {
|
||||||
|
when(mockUserService.deleteUser(any))
|
||||||
|
.thenAnswer((_) async {});
|
||||||
|
} else {
|
||||||
|
when(mockUserService.deleteUser(any))
|
||||||
|
.thenThrow(
|
||||||
|
Exception('사용자 삭제 중 오류가 발생했습니다.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// changeUserStatus
|
||||||
|
if (changeUserStatusSuccess) {
|
||||||
|
when(mockUserService.changeUserStatus(any, any))
|
||||||
|
.thenAnswer((invocation) async {
|
||||||
|
final id = invocation.positionalArguments[0] as int;
|
||||||
|
final isActive = invocation.positionalArguments[1] as bool;
|
||||||
|
return MockDataHelpers.createMockUserModel(
|
||||||
|
id: id,
|
||||||
|
isActive: isActive,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
when(mockUserService.changeUserStatus(any, any))
|
||||||
|
.thenThrow(
|
||||||
|
Exception('사용자 상태 변경 중 오류가 발생했습니다.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1447
test/helpers/simple_mock_services.mocks.dart
Normal file
1447
test/helpers/simple_mock_services.mocks.dart
Normal file
File diff suppressed because it is too large
Load Diff
296
test/helpers/test_helpers.dart
Normal file
296
test/helpers/test_helpers.dart
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
/// 테스트용 GetIt 인스턴스 초기화
|
||||||
|
GetIt setupTestGetIt() {
|
||||||
|
final getIt = GetIt.instance;
|
||||||
|
|
||||||
|
// 기존 등록된 서비스들 모두 제거
|
||||||
|
getIt.reset();
|
||||||
|
|
||||||
|
return getIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 테스트용 위젯 래퍼
|
||||||
|
/// 모든 위젯 테스트에서 필요한 기본 설정을 제공
|
||||||
|
class TestWidgetWrapper extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
final List<ChangeNotifierProvider>? providers;
|
||||||
|
final NavigatorObserver? navigatorObserver;
|
||||||
|
final Map<String, WidgetBuilder>? routes;
|
||||||
|
final String? initialRoute;
|
||||||
|
|
||||||
|
const TestWidgetWrapper({
|
||||||
|
Key? key,
|
||||||
|
required this.child,
|
||||||
|
this.providers,
|
||||||
|
this.navigatorObserver,
|
||||||
|
this.routes,
|
||||||
|
this.initialRoute,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget wrappedChild = MaterialApp(
|
||||||
|
title: 'Test App',
|
||||||
|
theme: ThemeData(
|
||||||
|
primarySwatch: Colors.blue,
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
|
localizationsDelegates: const [
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: const [
|
||||||
|
Locale('ko', 'KR'),
|
||||||
|
Locale('en', 'US'),
|
||||||
|
],
|
||||||
|
home: Scaffold(body: child),
|
||||||
|
routes: routes ?? {},
|
||||||
|
initialRoute: initialRoute,
|
||||||
|
navigatorObservers: navigatorObserver != null ? [navigatorObserver!] : [],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Provider가 있는 경우 래핑
|
||||||
|
if (providers != null && providers!.isNotEmpty) {
|
||||||
|
return MultiProvider(
|
||||||
|
providers: providers!,
|
||||||
|
child: wrappedChild,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrappedChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 위젯을 테스트 환경에서 펌프하는 헬퍼 함수
|
||||||
|
Future<void> pumpTestWidget(
|
||||||
|
WidgetTester tester,
|
||||||
|
Widget widget, {
|
||||||
|
List<ChangeNotifierProvider>? providers,
|
||||||
|
NavigatorObserver? navigatorObserver,
|
||||||
|
Map<String, WidgetBuilder>? routes,
|
||||||
|
String? initialRoute,
|
||||||
|
}) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
TestWidgetWrapper(
|
||||||
|
child: widget,
|
||||||
|
providers: providers,
|
||||||
|
navigatorObserver: navigatorObserver,
|
||||||
|
routes: routes,
|
||||||
|
initialRoute: initialRoute,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 비동기 작업을 기다리고 위젯을 리빌드하는 헬퍼
|
||||||
|
Future<void> pumpAndSettleWithTimeout(
|
||||||
|
WidgetTester tester, {
|
||||||
|
Duration timeout = const Duration(seconds: 10),
|
||||||
|
}) async {
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pumpAndSettle(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TextField에 텍스트를 입력하는 헬퍼
|
||||||
|
Future<void> enterTextByLabel(
|
||||||
|
WidgetTester tester,
|
||||||
|
String label,
|
||||||
|
String text,
|
||||||
|
) async {
|
||||||
|
final textFieldFinder = find.ancestor(
|
||||||
|
of: find.text(label),
|
||||||
|
matching: find.byType(TextFormField),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (textFieldFinder.evaluate().isEmpty) {
|
||||||
|
// 라벨로 찾지 못한 경우, 가까운 TextFormField 찾기
|
||||||
|
final labelWidget = find.text(label);
|
||||||
|
final textField = find.byType(TextFormField).first;
|
||||||
|
await tester.enterText(textField, text);
|
||||||
|
} else {
|
||||||
|
await tester.enterText(textFieldFinder, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 버튼을 찾고 탭하는 헬퍼
|
||||||
|
Future<void> tapButtonByText(
|
||||||
|
WidgetTester tester,
|
||||||
|
String buttonText,
|
||||||
|
) async {
|
||||||
|
final buttonFinder = find.widgetWithText(ElevatedButton, buttonText);
|
||||||
|
|
||||||
|
if (buttonFinder.evaluate().isEmpty) {
|
||||||
|
// ElevatedButton이 아닌 경우 다른 버튼 타입 시도
|
||||||
|
final textButtonFinder = find.widgetWithText(TextButton, buttonText);
|
||||||
|
if (textButtonFinder.evaluate().isNotEmpty) {
|
||||||
|
await tester.tap(textButtonFinder);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final outlinedButtonFinder = find.widgetWithText(OutlinedButton, buttonText);
|
||||||
|
if (outlinedButtonFinder.evaluate().isNotEmpty) {
|
||||||
|
await tester.tap(outlinedButtonFinder);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 아무 버튼도 찾지 못한 경우 텍스트만으로 시도
|
||||||
|
await tester.tap(find.text(buttonText));
|
||||||
|
} else {
|
||||||
|
await tester.tap(buttonFinder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 스낵바 메시지 검증 헬퍼
|
||||||
|
void expectSnackBar(WidgetTester tester, String message) {
|
||||||
|
expect(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byType(SnackBar),
|
||||||
|
matching: find.text(message),
|
||||||
|
),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 로딩 인디케이터 검증 헬퍼
|
||||||
|
void expectLoading(WidgetTester tester, {bool isLoading = true}) {
|
||||||
|
expect(
|
||||||
|
find.byType(CircularProgressIndicator),
|
||||||
|
isLoading ? findsOneWidget : findsNothing,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 에러 메시지 검증 헬퍼
|
||||||
|
void expectErrorMessage(WidgetTester tester, String errorMessage) {
|
||||||
|
expect(find.text(errorMessage), findsOneWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 화면 전환 대기 헬퍼
|
||||||
|
Future<void> waitForNavigation(WidgetTester tester) async {
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 300)); // 애니메이션 대기
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 다이얼로그 검증 헬퍼
|
||||||
|
void expectDialog(WidgetTester tester, {String? title, String? content}) {
|
||||||
|
expect(find.byType(Dialog), findsOneWidget);
|
||||||
|
|
||||||
|
if (title != null) {
|
||||||
|
expect(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byType(Dialog),
|
||||||
|
matching: find.text(title),
|
||||||
|
),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content != null) {
|
||||||
|
expect(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byType(Dialog),
|
||||||
|
matching: find.text(content),
|
||||||
|
),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 다이얼로그 닫기 헬퍼
|
||||||
|
Future<void> closeDialog(WidgetTester tester) async {
|
||||||
|
// 다이얼로그 외부 탭하여 닫기
|
||||||
|
await tester.tapAt(const Offset(10, 10));
|
||||||
|
await tester.pump();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 스크롤하여 위젯 찾기 헬퍼
|
||||||
|
Future<void> scrollUntilVisible(
|
||||||
|
WidgetTester tester,
|
||||||
|
Finder finder, {
|
||||||
|
double delta = 300,
|
||||||
|
int maxScrolls = 10,
|
||||||
|
Finder? scrollable,
|
||||||
|
}) async {
|
||||||
|
final scrollableFinder = scrollable ?? find.byType(Scrollable).first;
|
||||||
|
|
||||||
|
for (int i = 0; i < maxScrolls; i++) {
|
||||||
|
if (finder.evaluate().isNotEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.drag(scrollableFinder, Offset(0, -delta));
|
||||||
|
await tester.pump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 테이블이나 리스트에서 특정 행 찾기 헬퍼
|
||||||
|
Finder findRowContaining(String text) {
|
||||||
|
return find.ancestor(
|
||||||
|
of: find.text(text),
|
||||||
|
matching: find.byType(Row),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 폼 필드 검증 헬퍼
|
||||||
|
void expectFormFieldError(WidgetTester tester, String fieldLabel, String errorText) {
|
||||||
|
final formField = find.ancestor(
|
||||||
|
of: find.text(fieldLabel),
|
||||||
|
matching: find.byType(TextFormField),
|
||||||
|
);
|
||||||
|
|
||||||
|
final errorFinder = find.descendant(
|
||||||
|
of: formField,
|
||||||
|
matching: find.text(errorText),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(errorFinder, findsOneWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 드롭다운 선택 헬퍼
|
||||||
|
Future<void> selectDropdownItem(
|
||||||
|
WidgetTester tester,
|
||||||
|
String dropdownLabel,
|
||||||
|
String itemText,
|
||||||
|
) async {
|
||||||
|
// 드롭다운 찾기
|
||||||
|
final dropdown = find.ancestor(
|
||||||
|
of: find.text(dropdownLabel),
|
||||||
|
matching: find.byType(DropdownButtonFormField),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 드롭다운 열기
|
||||||
|
await tester.tap(dropdown);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// 아이템 선택
|
||||||
|
await tester.tap(find.text(itemText).last);
|
||||||
|
await tester.pump();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 날짜 선택 헬퍼
|
||||||
|
Future<void> selectDate(
|
||||||
|
WidgetTester tester,
|
||||||
|
String dateFieldLabel,
|
||||||
|
DateTime date,
|
||||||
|
) async {
|
||||||
|
// 날짜 필드 탭
|
||||||
|
final dateField = find.ancestor(
|
||||||
|
of: find.text(dateFieldLabel),
|
||||||
|
matching: find.byType(TextFormField),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(dateField);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// 날짜 선택 (간단한 구현, 실제로는 더 복잡할 수 있음)
|
||||||
|
await tester.tap(find.text(date.day.toString()));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// 확인 버튼 탭
|
||||||
|
await tester.tap(find.text('확인'));
|
||||||
|
await tester.pump();
|
||||||
|
}
|
||||||
113
test/unit/controllers/company_list_controller_test.dart
Normal file
113
test/unit/controllers/company_list_controller_test.dart
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:superport/screens/company/controllers/company_list_controller.dart';
|
||||||
|
import 'package:superport/services/company_service.dart';
|
||||||
|
|
||||||
|
import '../../helpers/test_helpers.dart';
|
||||||
|
import '../../helpers/simple_mock_services.dart';
|
||||||
|
import '../../helpers/simple_mock_services.mocks.dart';
|
||||||
|
import '../../helpers/mock_data_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late CompanyListController controller;
|
||||||
|
late MockMockDataService mockDataService;
|
||||||
|
late MockCompanyService mockCompanyService;
|
||||||
|
late GetIt getIt;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
getIt = setupTestGetIt();
|
||||||
|
mockDataService = MockMockDataService();
|
||||||
|
mockCompanyService = MockCompanyService();
|
||||||
|
|
||||||
|
// GetIt에 CompanyService 등록
|
||||||
|
getIt.registerSingleton<CompanyService>(mockCompanyService);
|
||||||
|
|
||||||
|
// Mock 설정
|
||||||
|
SimpleMockServiceHelpers.setupMockDataServiceMock(mockDataService);
|
||||||
|
SimpleMockServiceHelpers.setupCompanyServiceMock(mockCompanyService);
|
||||||
|
|
||||||
|
controller = CompanyListController(dataService: mockDataService);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
controller.dispose();
|
||||||
|
getIt.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
group('CompanyListController 단위 테스트', () {
|
||||||
|
test('검색 키워드 업데이트 테스트', () async {
|
||||||
|
// Act
|
||||||
|
await controller.updateSearchKeyword('테스트');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.searchKeyword, '테스트');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('회사 선택/해제 테스트', () {
|
||||||
|
// Act
|
||||||
|
controller.toggleCompanySelection(1);
|
||||||
|
expect(controller.selectedCompanyIds.contains(1), true);
|
||||||
|
|
||||||
|
controller.toggleCompanySelection(1);
|
||||||
|
expect(controller.selectedCompanyIds.contains(1), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('전체 선택/해제 테스트', () {
|
||||||
|
// Arrange
|
||||||
|
controller.companies = MockDataHelpers.createMockCompanyList(count: 3);
|
||||||
|
controller.filteredCompanies = controller.companies;
|
||||||
|
|
||||||
|
// Act - 전체 선택
|
||||||
|
controller.toggleSelectAll();
|
||||||
|
expect(controller.selectedCompanyIds.length, 3);
|
||||||
|
|
||||||
|
// Act - 전체 해제
|
||||||
|
controller.toggleSelectAll();
|
||||||
|
expect(controller.selectedCompanyIds.isEmpty, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('필터 적용 테스트', () {
|
||||||
|
// Arrange
|
||||||
|
controller.companies = MockDataHelpers.createMockCompanyList(count: 5);
|
||||||
|
controller.searchKeyword = '회사 1';
|
||||||
|
|
||||||
|
// Act
|
||||||
|
controller.applyFilters();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.filteredCompanies.length, 1);
|
||||||
|
expect(controller.filteredCompanies.first.name, '테스트 회사 1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('회사 삭제 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
controller.companies = MockDataHelpers.createMockCompanyList(count: 3);
|
||||||
|
controller.filteredCompanies = controller.companies;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final result = await controller.deleteCompany(1);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result, true);
|
||||||
|
expect(controller.companies.length, 2);
|
||||||
|
expect(controller.companies.any((c) => c.id == 1), false);
|
||||||
|
verify(mockCompanyService.deleteCompany(1)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('에러 처리 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
SimpleMockServiceHelpers.setupCompanyServiceMock(
|
||||||
|
mockCompanyService,
|
||||||
|
getCompaniesSuccess: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await controller.loadData();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.error, isNotNull);
|
||||||
|
expect(controller.isLoading, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
132
test/unit/controllers/equipment_list_controller_test.dart
Normal file
132
test/unit/controllers/equipment_list_controller_test.dart
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:superport/screens/equipment/controllers/equipment_list_controller.dart';
|
||||||
|
import 'package:superport/services/equipment_service.dart';
|
||||||
|
|
||||||
|
import '../../helpers/test_helpers.dart';
|
||||||
|
import '../../helpers/simple_mock_services.dart';
|
||||||
|
import '../../helpers/simple_mock_services.mocks.dart';
|
||||||
|
import '../../helpers/mock_data_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late EquipmentListController controller;
|
||||||
|
late MockMockDataService mockDataService;
|
||||||
|
late MockEquipmentService mockEquipmentService;
|
||||||
|
late GetIt getIt;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
getIt = setupTestGetIt();
|
||||||
|
mockDataService = MockMockDataService();
|
||||||
|
mockEquipmentService = MockEquipmentService();
|
||||||
|
|
||||||
|
// GetIt에 EquipmentService 등록
|
||||||
|
getIt.registerSingleton<EquipmentService>(mockEquipmentService);
|
||||||
|
|
||||||
|
// Mock 설정
|
||||||
|
when(mockDataService.getAllEquipments()).thenReturn(
|
||||||
|
MockDataHelpers.createMockUnifiedEquipmentList(count: 5)
|
||||||
|
);
|
||||||
|
|
||||||
|
// EquipmentService mock 설정
|
||||||
|
when(mockEquipmentService.getEquipments(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
status: anyNamed('status'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
warehouseLocationId: anyNamed('warehouseLocationId'),
|
||||||
|
)).thenAnswer((_) async => []);
|
||||||
|
|
||||||
|
when(mockEquipmentService.deleteEquipment(any))
|
||||||
|
.thenAnswer((_) async {});
|
||||||
|
|
||||||
|
controller = EquipmentListController(dataService: mockDataService);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
controller.dispose();
|
||||||
|
getIt.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
group('EquipmentListController 단위 테스트', () {
|
||||||
|
test('장비 선택/해제 테스트', () {
|
||||||
|
// Arrange
|
||||||
|
final equipment = MockDataHelpers.createMockUnifiedEquipment(id: 1);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
controller.selectEquipment(equipment.id, equipment.status, true);
|
||||||
|
expect(controller.selectedEquipmentIds.contains('${equipment.id}:${equipment.status}'), true);
|
||||||
|
|
||||||
|
controller.selectEquipment(equipment.id, equipment.status, false);
|
||||||
|
expect(controller.selectedEquipmentIds.contains('${equipment.id}:${equipment.status}'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('전체 선택 테스트', () {
|
||||||
|
// Arrange
|
||||||
|
controller.equipments = MockDataHelpers.createMockUnifiedEquipmentList(count: 3);
|
||||||
|
|
||||||
|
// 수동으로 전체 선택
|
||||||
|
for (final equipment in controller.equipments) {
|
||||||
|
controller.selectEquipment(equipment.id, equipment.status, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.selectedEquipmentIds.length, 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('상태 필터 변경 테스트', () async {
|
||||||
|
// Act
|
||||||
|
await controller.changeStatusFilter('IN_STOCK');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.selectedStatusFilter, 'IN_STOCK');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('장비 삭제 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
controller.equipments = MockDataHelpers.createMockUnifiedEquipmentList(count: 3);
|
||||||
|
final equipmentToDelete = controller.equipments.first;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final result = await controller.deleteEquipment(equipmentToDelete);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result, true);
|
||||||
|
expect(controller.equipments.length, 2);
|
||||||
|
expect(controller.equipments.any((e) => e.id == equipmentToDelete.id), false);
|
||||||
|
verify(mockEquipmentService.deleteEquipment(equipmentToDelete.equipment.id!)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('선택된 장비 수 테스트', () {
|
||||||
|
// Arrange
|
||||||
|
controller.equipments = MockDataHelpers.createMockUnifiedEquipmentList(count: 5);
|
||||||
|
|
||||||
|
// 3개만 선택
|
||||||
|
controller.selectEquipment(1, 'I', true);
|
||||||
|
controller.selectEquipment(2, 'I', true);
|
||||||
|
controller.selectEquipment(3, 'I', true);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.getSelectedEquipmentCount(), 3);
|
||||||
|
expect(controller.getSelectedInStockCount(), 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('에러 처리 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
when(mockEquipmentService.getEquipments(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
status: anyNamed('status'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
warehouseLocationId: anyNamed('warehouseLocationId'),
|
||||||
|
)).thenThrow(Exception('장비 목록을 불러오는 중 오류가 발생했습니다.'));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await controller.loadData();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.error, isNotNull);
|
||||||
|
expect(controller.isLoading, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
255
test/unit/controllers/user_list_controller_test.dart
Normal file
255
test/unit/controllers/user_list_controller_test.dart
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:superport/screens/user/controllers/user_list_controller.dart';
|
||||||
|
import 'package:superport/services/user_service.dart';
|
||||||
|
import 'package:superport/models/company_model.dart';
|
||||||
|
|
||||||
|
import '../../helpers/test_helpers.dart';
|
||||||
|
import '../../helpers/simple_mock_services.dart';
|
||||||
|
import '../../helpers/simple_mock_services.mocks.dart';
|
||||||
|
import '../../helpers/mock_data_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late UserListController controller;
|
||||||
|
late MockMockDataService mockDataService;
|
||||||
|
late MockUserService mockUserService;
|
||||||
|
late GetIt getIt;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
getIt = setupTestGetIt();
|
||||||
|
mockDataService = MockMockDataService();
|
||||||
|
mockUserService = MockUserService();
|
||||||
|
|
||||||
|
// GetIt에 UserService 등록
|
||||||
|
getIt.registerSingleton<UserService>(mockUserService);
|
||||||
|
|
||||||
|
// Mock 설정
|
||||||
|
SimpleMockServiceHelpers.setupMockDataServiceMock(mockDataService);
|
||||||
|
SimpleMockServiceHelpers.setupUserServiceMock(mockUserService);
|
||||||
|
|
||||||
|
controller = UserListController(dataService: mockDataService);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
controller.dispose();
|
||||||
|
getIt.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
group('UserListController 단위 테스트', () {
|
||||||
|
test('초기 상태 확인', () {
|
||||||
|
expect(controller.users, isEmpty);
|
||||||
|
expect(controller.isLoading, false);
|
||||||
|
expect(controller.error, isNull);
|
||||||
|
expect(controller.hasMoreData, true);
|
||||||
|
expect(controller.searchQuery, '');
|
||||||
|
expect(controller.filterCompanyId, isNull);
|
||||||
|
expect(controller.filterRole, isNull);
|
||||||
|
expect(controller.filterIsActive, isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('사용자 목록 로드 테스트', () async {
|
||||||
|
// Act
|
||||||
|
await controller.loadUsers();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.users, isNotEmpty);
|
||||||
|
expect(controller.users.length, 10);
|
||||||
|
expect(controller.isLoading, false);
|
||||||
|
expect(controller.error, isNull);
|
||||||
|
verify(mockUserService.getUsers(
|
||||||
|
page: 1,
|
||||||
|
perPage: 20,
|
||||||
|
isActive: null,
|
||||||
|
companyId: null,
|
||||||
|
role: null,
|
||||||
|
)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('검색 쿼리 설정 및 검색 테스트', () async {
|
||||||
|
// Act
|
||||||
|
controller.setSearchQuery('사용자 1');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.searchQuery, '사용자 1');
|
||||||
|
await Future.delayed(Duration(milliseconds: 100)); // loadUsers 완료 대기
|
||||||
|
verify(mockUserService.getUsers(
|
||||||
|
page: 1,
|
||||||
|
perPage: 20,
|
||||||
|
isActive: null,
|
||||||
|
companyId: null,
|
||||||
|
role: null,
|
||||||
|
)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('필터 설정 테스트', () async {
|
||||||
|
// Act
|
||||||
|
controller.setFilters(
|
||||||
|
companyId: 1,
|
||||||
|
role: 'S',
|
||||||
|
isActive: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.filterCompanyId, 1);
|
||||||
|
expect(controller.filterRole, 'S');
|
||||||
|
expect(controller.filterIsActive, true);
|
||||||
|
await Future.delayed(Duration(milliseconds: 100)); // loadUsers 완료 대기
|
||||||
|
verify(mockUserService.getUsers(
|
||||||
|
page: 1,
|
||||||
|
perPage: 20,
|
||||||
|
isActive: true,
|
||||||
|
companyId: 1,
|
||||||
|
role: 'S',
|
||||||
|
)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('필터 초기화 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
controller.setFilters(
|
||||||
|
companyId: 1,
|
||||||
|
role: 'S',
|
||||||
|
isActive: true,
|
||||||
|
);
|
||||||
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
controller.clearFilters();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.filterCompanyId, isNull);
|
||||||
|
expect(controller.filterRole, isNull);
|
||||||
|
expect(controller.filterIsActive, isNull);
|
||||||
|
expect(controller.searchQuery, '');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('사용자 삭제 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
await controller.loadUsers();
|
||||||
|
final initialUserCount = controller.users.length;
|
||||||
|
bool deletedCallbackCalled = false;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await controller.deleteUser(
|
||||||
|
1,
|
||||||
|
() => deletedCallbackCalled = true,
|
||||||
|
(error) => fail('삭제 실패: $error'),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(deletedCallbackCalled, true);
|
||||||
|
expect(controller.users.length, initialUserCount - 1);
|
||||||
|
expect(controller.users.any((u) => u.id == 1), false);
|
||||||
|
verify(mockUserService.deleteUser(1)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('사용자 상태 변경 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
await controller.loadUsers();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await controller.changeUserStatus(
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
(error) => fail('상태 변경 실패: $error'),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
final updatedUser = controller.users.firstWhere((u) => u.id == 1);
|
||||||
|
expect(updatedUser.isActive, false);
|
||||||
|
verify(mockUserService.changeUserStatus(1, false)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('에러 처리 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
SimpleMockServiceHelpers.setupUserServiceMock(
|
||||||
|
mockUserService,
|
||||||
|
getUsersSuccess: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await controller.loadUsers();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.error, isNotNull);
|
||||||
|
expect(controller.isLoading, false);
|
||||||
|
expect(controller.users, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('페이지네이션 - 더 불러오기 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
// 첫 번째 페이지와 두 번째 페이지에 대해 다른 응답 설정
|
||||||
|
when(mockUserService.getUsers(
|
||||||
|
page: 1,
|
||||||
|
perPage: 20,
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
role: anyNamed('role'),
|
||||||
|
)).thenAnswer((_) async =>
|
||||||
|
MockDataHelpers.createMockUserModelList(count: 20),
|
||||||
|
);
|
||||||
|
|
||||||
|
when(mockUserService.getUsers(
|
||||||
|
page: 2,
|
||||||
|
perPage: 20,
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
companyId: anyNamed('companyId'),
|
||||||
|
role: anyNamed('role'),
|
||||||
|
)).thenAnswer((_) async =>
|
||||||
|
MockDataHelpers.createMockUserModelList(count: 10),
|
||||||
|
);
|
||||||
|
|
||||||
|
await controller.loadUsers();
|
||||||
|
final initialCount = controller.users.length;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await controller.loadMore();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.users.length, greaterThan(initialCount));
|
||||||
|
verify(mockUserService.getUsers(
|
||||||
|
page: 2,
|
||||||
|
perPage: 20,
|
||||||
|
isActive: null,
|
||||||
|
companyId: null,
|
||||||
|
role: null,
|
||||||
|
)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Mock 모드에서 필터링 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
controller.toggleApiMode(); // Mock 모드로 전환
|
||||||
|
|
||||||
|
// Act
|
||||||
|
controller.setFilters(companyId: 1, role: 'S');
|
||||||
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Mock 모드에서는 getAllUsers를 통해 전체 데이터를 가져온 후 필터링
|
||||||
|
verify(mockDataService.getAllUsers()).called(greaterThanOrEqualTo(1));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('지점명 조회 테스트', () {
|
||||||
|
// Arrange
|
||||||
|
final mockCompany = Company(
|
||||||
|
id: 1,
|
||||||
|
name: '테스트 회사',
|
||||||
|
branches: [
|
||||||
|
Branch(
|
||||||
|
id: 1,
|
||||||
|
companyId: 1,
|
||||||
|
name: '본사',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
when(mockDataService.getCompanyById(1)).thenReturn(mockCompany);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final branchName = controller.getBranchName(1, 1);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(branchName, '본사');
|
||||||
|
verify(mockDataService.getCompanyById(1)).called(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
413
test/widget/screens/company_list_widget_test.dart
Normal file
413
test/widget/screens/company_list_widget_test.dart
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:superport/screens/company/company_list_redesign.dart';
|
||||||
|
import 'package:superport/screens/company/controllers/company_list_controller.dart';
|
||||||
|
import 'package:superport/services/company_service.dart';
|
||||||
|
import 'package:superport/services/auth_service.dart';
|
||||||
|
import 'package:superport/services/mock_data_service.dart';
|
||||||
|
import 'package:superport/models/company_model.dart';
|
||||||
|
import 'package:superport/models/address_model.dart';
|
||||||
|
import 'package:superport/core/errors/failures.dart';
|
||||||
|
|
||||||
|
import '../../helpers/test_helpers.dart';
|
||||||
|
import '../../helpers/simple_mock_services.dart';
|
||||||
|
import '../../helpers/simple_mock_services.mocks.dart';
|
||||||
|
import '../../helpers/mock_data_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late MockCompanyService mockCompanyService;
|
||||||
|
late MockAuthService mockAuthService;
|
||||||
|
late MockMockDataService mockDataService;
|
||||||
|
late GetIt getIt;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
// GetIt 초기화
|
||||||
|
getIt = setupTestGetIt();
|
||||||
|
|
||||||
|
// Mock 서비스 생성
|
||||||
|
mockCompanyService = MockCompanyService();
|
||||||
|
mockAuthService = MockAuthService();
|
||||||
|
mockDataService = MockMockDataService();
|
||||||
|
|
||||||
|
// Mock 서비스 등록
|
||||||
|
getIt.registerSingleton<CompanyService>(mockCompanyService);
|
||||||
|
getIt.registerSingleton<AuthService>(mockAuthService);
|
||||||
|
getIt.registerSingleton<MockDataService>(mockDataService);
|
||||||
|
|
||||||
|
// 기본 Mock 설정
|
||||||
|
SimpleMockServiceHelpers.setupAuthServiceMock(mockAuthService, isLoggedIn: true);
|
||||||
|
SimpleMockServiceHelpers.setupCompanyServiceMock(mockCompanyService);
|
||||||
|
SimpleMockServiceHelpers.setupMockDataServiceMock(mockDataService);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
getIt.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
group('회사 목록 화면 Widget 테스트', () {
|
||||||
|
testWidgets('초기 화면 렌더링 테스트', (WidgetTester tester) async {
|
||||||
|
// Arrange & Act
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(find.text('회사 관리'), findsOneWidget); // 앱바 타이틀
|
||||||
|
expect(find.byType(TextField), findsOneWidget); // 검색 필드
|
||||||
|
expect(find.byIcon(Icons.add), findsOneWidget); // 추가 버튼
|
||||||
|
expect(find.byType(DataTable), findsOneWidget); // 데이터 테이블
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('회사 목록 로딩 및 표시 테스트', (WidgetTester tester) async {
|
||||||
|
// Arrange
|
||||||
|
final mockCompanies = MockDataHelpers.createMockCompanyList(count: 5);
|
||||||
|
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async => mockCompanies);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// 각 회사가 테이블에 표시되는지 확인
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
expect(find.text('테스트 회사 ${i + 1}'), findsOneWidget);
|
||||||
|
expect(find.text('담당자 ${i + 1}'), findsOneWidget);
|
||||||
|
expect(find.text('02-${1000 + i}-${5678 + i}'), findsOneWidget);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('회사 검색 기능 테스트', (WidgetTester tester) async {
|
||||||
|
// Arrange
|
||||||
|
final allCompanies = MockDataHelpers.createMockCompanyList(count: 10);
|
||||||
|
final searchedCompanies = [allCompanies[0]]; // 검색 결과로 첫 번째 회사만
|
||||||
|
|
||||||
|
// 초기 로드
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async => allCompanies);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// 검색어 입력
|
||||||
|
final searchField = find.byType(TextField);
|
||||||
|
await tester.enterText(searchField, '테스트 회사 1');
|
||||||
|
|
||||||
|
// 검색 결과 설정
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: '테스트 회사 1',
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async => searchedCompanies);
|
||||||
|
|
||||||
|
// 디바운스 대기
|
||||||
|
await tester.pump(const Duration(milliseconds: 600));
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(find.text('테스트 회사 1'), findsOneWidget);
|
||||||
|
expect(find.text('테스트 회사 2'), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('회사 추가 버튼 클릭 테스트', (WidgetTester tester) async {
|
||||||
|
// Arrange
|
||||||
|
bool navigated = false;
|
||||||
|
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
routes: {
|
||||||
|
'/company/add': (context) {
|
||||||
|
navigated = true;
|
||||||
|
return const Scaffold(body: Text('회사 추가 화면'));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final addButton = find.byIcon(Icons.add);
|
||||||
|
await tester.tap(addButton);
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(navigated, true);
|
||||||
|
expect(find.text('회사 추가 화면'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('회사 삭제 다이얼로그 테스트', (WidgetTester tester) async {
|
||||||
|
// Arrange
|
||||||
|
final companies = MockDataHelpers.createMockCompanyList(count: 1);
|
||||||
|
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async => companies);
|
||||||
|
|
||||||
|
when(mockCompanyService.deleteCompany(any))
|
||||||
|
.thenAnswer((_) async => null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// 삭제 버튼 찾기 및 클릭
|
||||||
|
final deleteButton = find.byIcon(Icons.delete).first;
|
||||||
|
await tester.tap(deleteButton);
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Assert - 삭제 확인 다이얼로그
|
||||||
|
expectDialog(tester, title: '삭제 확인', content: '이 회사 정보를 삭제하시겠습니까?');
|
||||||
|
|
||||||
|
// 삭제 확인
|
||||||
|
await tapButtonByText(tester, '삭제');
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// 삭제 메서드 호출 확인
|
||||||
|
verify(mockCompanyService.deleteCompany(1)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('회사 정보 수정 화면 이동 테스트', (WidgetTester tester) async {
|
||||||
|
// Arrange
|
||||||
|
final companies = MockDataHelpers.createMockCompanyList(count: 1);
|
||||||
|
bool navigated = false;
|
||||||
|
int? companyId;
|
||||||
|
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async => companies);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
routes: {
|
||||||
|
'/company/edit': (context) {
|
||||||
|
navigated = true;
|
||||||
|
companyId = ModalRoute.of(context)!.settings.arguments as int?;
|
||||||
|
return const Scaffold(body: Text('회사 수정 화면'));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// 수정 버튼 찾기 및 클릭
|
||||||
|
final editButton = find.byIcon(Icons.edit).first;
|
||||||
|
await tester.tap(editButton);
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(navigated, true);
|
||||||
|
expect(companyId, 1);
|
||||||
|
expect(find.text('회사 수정 화면'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('회사 목록 페이지네이션 테스트', (WidgetTester tester) async {
|
||||||
|
// Arrange
|
||||||
|
final firstPageCompanies = MockDataHelpers.createMockCompanyList(count: 20);
|
||||||
|
final secondPageCompanies = MockDataHelpers.createMockCompanyList(count: 5)
|
||||||
|
.map((c) => MockDataHelpers.createMockCompany(
|
||||||
|
id: c.id! + 20,
|
||||||
|
name: '추가 회사 ${c.id}',
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 첫 페이지
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: 1,
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async => firstPageCompanies);
|
||||||
|
|
||||||
|
// 두 번째 페이지
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: 2,
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async => secondPageCompanies);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// 스크롤하여 더 많은 데이터 로드
|
||||||
|
final scrollable = find.byType(SingleChildScrollView).first;
|
||||||
|
await tester.drag(scrollable, const Offset(0, -500));
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify(mockCompanyService.getCompanies(
|
||||||
|
page: 1,
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).called(greaterThanOrEqualTo(1));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('에러 처리 테스트', (WidgetTester tester) async {
|
||||||
|
// Arrange
|
||||||
|
SimpleMockServiceHelpers.setupCompanyServiceMock(
|
||||||
|
mockCompanyService,
|
||||||
|
getCompaniesSuccess: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(find.text('회사 목록을 불러오는 중 오류가 발생했습니다.'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('로딩 상태 표시 테스트', (WidgetTester tester) async {
|
||||||
|
// Arrange
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async {
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
return MockDataHelpers.createMockCompanyList(count: 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pump(); // 로딩 시작
|
||||||
|
|
||||||
|
// Assert - 로딩 인디케이터 표시
|
||||||
|
expectLoading(tester, isLoading: true);
|
||||||
|
|
||||||
|
// 로딩 완료 대기
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Assert - 로딩 인디케이터 사라짐
|
||||||
|
expectLoading(tester, isLoading: false);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('회사 선택 체크박스 테스트', (WidgetTester tester) async {
|
||||||
|
// Arrange
|
||||||
|
final companies = MockDataHelpers.createMockCompanyList(count: 3);
|
||||||
|
|
||||||
|
when(mockCompanyService.getCompanies(
|
||||||
|
page: anyNamed('page'),
|
||||||
|
perPage: anyNamed('perPage'),
|
||||||
|
search: anyNamed('search'),
|
||||||
|
isActive: anyNamed('isActive'),
|
||||||
|
)).thenAnswer((_) async => companies);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await pumpTestWidget(
|
||||||
|
tester,
|
||||||
|
const CompanyListRedesign(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// 첫 번째 체크박스 선택
|
||||||
|
final firstCheckbox = find.byType(Checkbox).at(1); // 헤더 체크박스 제외
|
||||||
|
await tester.tap(firstCheckbox);
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// 전체 선택 체크박스 클릭
|
||||||
|
final selectAllCheckbox = find.byType(Checkbox).first;
|
||||||
|
await tester.tap(selectAllCheckbox);
|
||||||
|
await pumpAndSettleWithTimeout(tester);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// 모든 체크박스가 선택되었는지 확인하는 로직 추가
|
||||||
|
final checkboxes = find.byType(Checkbox);
|
||||||
|
expect(checkboxes, findsNWidgets(4)); // 헤더 + 3개 회사
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('회사 컨트롤러 단위 테스트', () {
|
||||||
|
test('검색 키워드 업데이트 테스트', () async {
|
||||||
|
// Arrange
|
||||||
|
final controller = CompanyListController(dataService: MockMockDataService());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
controller.updateSearchKeyword('테스트');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(controller.searchKeyword, '테스트');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('회사 선택/해제 테스트', () {
|
||||||
|
// Arrange
|
||||||
|
final controller = CompanyListController(dataService: MockMockDataService());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
controller.toggleCompanySelection(1);
|
||||||
|
expect(controller.selectedCompanyIds.contains(1), true);
|
||||||
|
|
||||||
|
controller.toggleCompanySelection(1);
|
||||||
|
expect(controller.selectedCompanyIds.contains(1), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('전체 선택/해제 테스트', () {
|
||||||
|
// Arrange
|
||||||
|
final controller = CompanyListController(dataService: MockMockDataService());
|
||||||
|
controller.companies = MockDataHelpers.createMockCompanyList(count: 3);
|
||||||
|
controller.filteredCompanies = controller.companies;
|
||||||
|
|
||||||
|
// Act - 전체 선택
|
||||||
|
controller.toggleSelectAll();
|
||||||
|
expect(controller.selectedCompanyIds.length, 3);
|
||||||
|
|
||||||
|
// Act - 전체 해제
|
||||||
|
controller.toggleSelectAll();
|
||||||
|
expect(controller.selectedCompanyIds.isEmpty, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user