test: 통합 테스트 오류 및 경고 수정
- 모든 서비스 메서드 시그니처를 실제 구현에 맞게 수정 - TestDataGenerator 제거하고 직접 객체 생성으로 변경 - 모델 필드명 및 타입 불일치 수정 - 불필요한 Either 패턴 사용 제거 - null safety 관련 이슈 해결 수정된 파일: - test/integration/screens/company_integration_test.dart - test/integration/screens/equipment_integration_test.dart - test/integration/screens/user_integration_test.dart - test/integration/screens/login_integration_test.dart
This commit is contained in:
214
test/integration/mock/login_flow_integration_test.dart
Normal file
214
test/integration/mock/login_flow_integration_test.dart
Normal file
@@ -0,0 +1,214 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:superport/data/models/auth/login_request.dart';
|
||||
import 'package:superport/data/models/auth/login_response.dart';
|
||||
import 'package:superport/data/models/auth/auth_user.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
import 'package:superport/data/models/auth/token_response.dart';
|
||||
import '../../helpers/test_helpers.dart';
|
||||
import 'package:superport/services/auth_service.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
// Mock AuthService
|
||||
class MockAuthService extends Mock implements AuthService {
|
||||
@override
|
||||
Stream<bool> get authStateChanges => const Stream.empty();
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('로그인 플로우 Integration 테스트', () {
|
||||
late MockAuthService mockAuthService;
|
||||
final getIt = GetIt.instance;
|
||||
|
||||
setUp(() {
|
||||
setupTestGetIt();
|
||||
mockAuthService = MockAuthService();
|
||||
|
||||
// Mock 서비스 등록
|
||||
getIt.registerSingleton<AuthService>(mockAuthService);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
getIt.reset();
|
||||
});
|
||||
|
||||
test('성공적인 로그인 플로우 - 로그인 → 토큰 저장 → 사용자 정보 조회', () async {
|
||||
// Arrange
|
||||
const loginRequest = LoginRequest(
|
||||
email: 'admin@superport.kr',
|
||||
password: 'admin123!',
|
||||
);
|
||||
|
||||
final loginResponse = LoginResponse(
|
||||
accessToken: 'test_access_token',
|
||||
refreshToken: 'test_refresh_token',
|
||||
tokenType: 'Bearer',
|
||||
expiresIn: 3600,
|
||||
user: AuthUser(
|
||||
id: 1,
|
||||
username: 'admin',
|
||||
email: 'admin@superport.kr',
|
||||
name: '관리자',
|
||||
role: 'S', // S: 관리자
|
||||
),
|
||||
);
|
||||
|
||||
// Mock 설정
|
||||
when(mockAuthService.login(loginRequest))
|
||||
.thenAnswer((_) async => Right(loginResponse));
|
||||
|
||||
when(mockAuthService.getAccessToken())
|
||||
.thenAnswer((_) async => 'test_access_token');
|
||||
|
||||
when(mockAuthService.getCurrentUser())
|
||||
.thenAnswer((_) async => loginResponse.user);
|
||||
|
||||
// Act - 로그인
|
||||
final loginResult = await mockAuthService.login(loginRequest);
|
||||
|
||||
// Assert - 로그인 성공
|
||||
expect(loginResult.isRight(), true);
|
||||
|
||||
loginResult.fold(
|
||||
(failure) => fail('로그인이 실패하면 안됩니다'),
|
||||
(response) {
|
||||
expect(response.accessToken, 'test_access_token');
|
||||
expect(response.user.email, 'admin@superport.kr');
|
||||
expect(response.user.role, 'S');
|
||||
},
|
||||
);
|
||||
|
||||
// Act - 토큰 조회
|
||||
final savedToken = await mockAuthService.getAccessToken();
|
||||
expect(savedToken, 'test_access_token');
|
||||
|
||||
// Act - 사용자 정보 조회
|
||||
final currentUser = await mockAuthService.getCurrentUser();
|
||||
expect(currentUser, isNotNull);
|
||||
expect(currentUser!.email, 'admin@superport.kr');
|
||||
|
||||
// Verify - 메서드 호출 확인
|
||||
verify(mockAuthService.login(loginRequest)).called(1);
|
||||
verify(mockAuthService.getAccessToken()).called(1);
|
||||
verify(mockAuthService.getCurrentUser()).called(1);
|
||||
});
|
||||
|
||||
test('로그인 실패 플로우 - 잘못된 인증 정보', () async {
|
||||
// Arrange
|
||||
const loginRequest = LoginRequest(
|
||||
email: 'wrong@email.com',
|
||||
password: 'wrongpassword',
|
||||
);
|
||||
|
||||
// Mock 설정
|
||||
when(mockAuthService.login(loginRequest))
|
||||
.thenAnswer((_) async => Left(
|
||||
AuthenticationFailure(
|
||||
message: '이메일 또는 비밀번호가 올바르지 않습니다.',
|
||||
),
|
||||
));
|
||||
|
||||
// Act
|
||||
final result = await mockAuthService.login(loginRequest);
|
||||
|
||||
// Assert
|
||||
expect(result.isLeft(), true);
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
expect(failure, isA<AuthenticationFailure>());
|
||||
expect(failure.message, contains('올바르지 않습니다'));
|
||||
},
|
||||
(_) => fail('로그인이 성공하면 안됩니다'),
|
||||
);
|
||||
});
|
||||
|
||||
test('로그아웃 플로우', () async {
|
||||
// Arrange - 먼저 로그인 상태 설정
|
||||
when(mockAuthService.getAccessToken())
|
||||
.thenAnswer((_) async => 'test_access_token');
|
||||
|
||||
when(mockAuthService.getCurrentUser())
|
||||
.thenAnswer((_) async => AuthUser(
|
||||
id: 1,
|
||||
username: 'admin',
|
||||
email: 'admin@superport.kr',
|
||||
name: '관리자',
|
||||
role: 'S',
|
||||
));
|
||||
|
||||
// 로그인 상태 확인
|
||||
expect(await mockAuthService.getAccessToken(), isNotNull);
|
||||
expect(await mockAuthService.getCurrentUser(), isNotNull);
|
||||
|
||||
// Mock 설정 - 로그아웃
|
||||
when(mockAuthService.logout()).thenAnswer((_) async => const Right(null));
|
||||
|
||||
// 로그아웃 후 상태 변경
|
||||
when(mockAuthService.getAccessToken())
|
||||
.thenAnswer((_) async => null);
|
||||
|
||||
when(mockAuthService.getCurrentUser())
|
||||
.thenAnswer((_) async => null);
|
||||
|
||||
// Act - 로그아웃
|
||||
await mockAuthService.logout();
|
||||
|
||||
// Assert - 로그아웃 확인
|
||||
expect(await mockAuthService.getAccessToken(), isNull);
|
||||
expect(await mockAuthService.getCurrentUser(), isNull);
|
||||
|
||||
// Verify
|
||||
verify(mockAuthService.logout()).called(1);
|
||||
});
|
||||
|
||||
test('토큰 갱신 플로우', () async {
|
||||
// Arrange
|
||||
const oldToken = 'old_access_token';
|
||||
const newToken = 'new_access_token';
|
||||
const refreshToken = 'test_refresh_token';
|
||||
|
||||
// Mock 설정 - 초기 토큰
|
||||
when(mockAuthService.getAccessToken())
|
||||
.thenAnswer((_) async => oldToken);
|
||||
|
||||
// getRefreshToken 메서드가 AuthService에 없으므로 제거
|
||||
|
||||
// Mock 설정 - 토큰 갱신
|
||||
when(mockAuthService.refreshToken())
|
||||
.thenAnswer((_) async => Right(
|
||||
TokenResponse(
|
||||
accessToken: newToken,
|
||||
refreshToken: refreshToken,
|
||||
tokenType: 'Bearer',
|
||||
expiresIn: 3600,
|
||||
),
|
||||
));
|
||||
|
||||
// 갱신 후 새 토큰 반환
|
||||
when(mockAuthService.getAccessToken())
|
||||
.thenAnswer((_) async => newToken);
|
||||
|
||||
// Act
|
||||
final refreshResult = await mockAuthService.refreshToken();
|
||||
|
||||
// Assert
|
||||
expect(refreshResult.isRight(), true);
|
||||
|
||||
refreshResult.fold(
|
||||
(failure) => fail('토큰 갱신이 실패하면 안됩니다'),
|
||||
(response) {
|
||||
expect(response.accessToken, newToken);
|
||||
},
|
||||
);
|
||||
|
||||
// 갱신 후 토큰 확인
|
||||
final currentToken = await mockAuthService.getAccessToken();
|
||||
expect(currentToken, newToken);
|
||||
|
||||
// Verify
|
||||
verify(mockAuthService.refreshToken()).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
93
test/integration/mock/mock_secure_storage.dart
Normal file
93
test/integration/mock/mock_secure_storage.dart
Normal file
@@ -0,0 +1,93 @@
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
/// 테스트를 위한 Mock SecureStorage
|
||||
class MockSecureStorage extends FlutterSecureStorage {
|
||||
final Map<String, String> _storage = {};
|
||||
|
||||
@override
|
||||
Future<void> write({
|
||||
required String key,
|
||||
required String? value,
|
||||
IOSOptions? iOptions,
|
||||
AndroidOptions? aOptions,
|
||||
LinuxOptions? lOptions,
|
||||
WebOptions? webOptions,
|
||||
MacOsOptions? mOptions,
|
||||
WindowsOptions? wOptions,
|
||||
}) async {
|
||||
if (value != null) {
|
||||
_storage[key] = value;
|
||||
// 디버깅용 print문 제거
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> read({
|
||||
required String key,
|
||||
IOSOptions? iOptions,
|
||||
AndroidOptions? aOptions,
|
||||
LinuxOptions? lOptions,
|
||||
WebOptions? webOptions,
|
||||
MacOsOptions? mOptions,
|
||||
WindowsOptions? wOptions,
|
||||
}) async {
|
||||
final value = _storage[key];
|
||||
// 디버깅용 print문 제거
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete({
|
||||
required String key,
|
||||
IOSOptions? iOptions,
|
||||
AndroidOptions? aOptions,
|
||||
LinuxOptions? lOptions,
|
||||
WebOptions? webOptions,
|
||||
MacOsOptions? mOptions,
|
||||
WindowsOptions? wOptions,
|
||||
}) async {
|
||||
_storage.remove(key);
|
||||
// 디버깅용 print문 제거
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAll({
|
||||
IOSOptions? iOptions,
|
||||
AndroidOptions? aOptions,
|
||||
LinuxOptions? lOptions,
|
||||
WebOptions? webOptions,
|
||||
MacOsOptions? mOptions,
|
||||
WindowsOptions? wOptions,
|
||||
}) async {
|
||||
_storage.clear();
|
||||
// 디버깅용 print문 제거
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> readAll({
|
||||
IOSOptions? iOptions,
|
||||
AndroidOptions? aOptions,
|
||||
LinuxOptions? lOptions,
|
||||
WebOptions? webOptions,
|
||||
MacOsOptions? mOptions,
|
||||
WindowsOptions? wOptions,
|
||||
}) async {
|
||||
// 디버깅용 print문 제거
|
||||
return Map<String, String>.from(_storage);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> containsKey({
|
||||
required String key,
|
||||
IOSOptions? iOptions,
|
||||
AndroidOptions? aOptions,
|
||||
LinuxOptions? lOptions,
|
||||
WebOptions? webOptions,
|
||||
MacOsOptions? mOptions,
|
||||
WindowsOptions? wOptions,
|
||||
}) async {
|
||||
final contains = _storage.containsKey(key);
|
||||
// 디버깅용 print문 제거
|
||||
return contains;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user