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:
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