결재 단계 목록 화면과 테스트 도입

This commit is contained in:
JiWoong Sul
2025-09-25 16:41:22 +09:00
parent 86d3f5bf21
commit 35b9002688
10 changed files with 981 additions and 24 deletions

View File

@@ -0,0 +1,144 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/features/approvals/domain/entities/approval.dart';
import 'package:superport_v2/features/approvals/step/domain/entities/approval_step_record.dart';
import 'package:superport_v2/features/approvals/step/domain/repositories/approval_step_repository.dart';
import 'package:superport_v2/features/approvals/step/presentation/controllers/approval_step_controller.dart';
class _MockApprovalStepRepository extends Mock
implements ApprovalStepRepository {}
void main() {
late ApprovalStepController controller;
late _MockApprovalStepRepository repository;
final sampleRecord = ApprovalStepRecord(
approvalId: 10,
approvalNo: 'APP-2024-0001',
transactionNo: 'TRX-2024-01',
templateName: '입고 기본',
step: ApprovalStep(
id: 100,
stepOrder: 1,
approver: ApprovalApprover(id: 21, employeeNo: 'E001', name: '최승인'),
status: ApprovalStatus(id: 1, name: '승인대기', color: null),
assignedAt: DateTime(2024, 4, 1, 9),
decidedAt: null,
note: '확인 요청',
),
);
PaginatedResult<ApprovalStepRecord> createResult(
List<ApprovalStepRecord> items,
) {
return PaginatedResult<ApprovalStepRecord>(
items: items,
page: 1,
pageSize: 20,
total: items.length,
);
}
setUp(() {
repository = _MockApprovalStepRepository();
controller = ApprovalStepController(repository: repository);
});
test('fetch 성공 시 결과를 갱신한다', () async {
when(
() => repository.list(
page: any(named: 'page'),
pageSize: any(named: 'pageSize'),
query: any(named: 'query'),
statusId: any(named: 'statusId'),
approverId: any(named: 'approverId'),
approvalId: any(named: 'approvalId'),
),
).thenAnswer((_) async => createResult([sampleRecord]));
await controller.fetch();
expect(controller.result?.items, isNotEmpty);
expect(controller.errorMessage, isNull);
verify(
() => repository.list(
page: 1,
pageSize: 20,
query: null,
statusId: null,
approverId: null,
approvalId: null,
),
).called(1);
});
test('에러 발생 시 errorMessage를 설정한다', () async {
when(
() => repository.list(
page: any(named: 'page'),
pageSize: any(named: 'pageSize'),
query: any(named: 'query'),
statusId: any(named: 'statusId'),
approverId: any(named: 'approverId'),
approvalId: any(named: 'approvalId'),
),
).thenThrow(Exception('fail'));
await controller.fetch();
expect(controller.errorMessage, isNotNull);
expect(controller.result, isNull);
});
test('필터 갱신 후 fetch 시 파라미터에 반영한다', () async {
when(
() => repository.list(
page: any(named: 'page'),
pageSize: any(named: 'pageSize'),
query: any(named: 'query'),
statusId: any(named: 'statusId'),
approverId: any(named: 'approverId'),
approvalId: any(named: 'approvalId'),
),
).thenAnswer((_) async => createResult([sampleRecord]));
controller.updateQuery('APP-2024');
controller.updateStatusId(2);
await controller.fetch(page: 3);
verify(
() => repository.list(
page: 3,
pageSize: 20,
query: 'APP-2024',
statusId: 2,
approverId: null,
approvalId: null,
),
).called(1);
});
test('fetchDetail 성공 시 selected가 설정된다', () async {
when(
() => repository.fetchDetail(any()),
).thenAnswer((_) async => sampleRecord);
final detail = await controller.fetchDetail(100);
expect(detail, isNotNull);
expect(controller.selected, isNotNull);
verify(() => repository.fetchDetail(100)).called(1);
});
test('fetchDetail 실패 시 null을 반환한다', () async {
when(() => repository.fetchDetail(any())).thenThrow(Exception('fail'));
final detail = await controller.fetchDetail(100);
expect(detail, isNull);
expect(controller.errorMessage, isNotNull);
});
}