이벤트 가져오기 위젯 테스트 추가 및 테스트 훅 도입

This commit is contained in:
2025-09-12 06:33:44 +09:00
parent 107abc963f
commit a5f2544d11
121 changed files with 39277 additions and 3911 deletions
@@ -11,6 +11,8 @@ import 'package:lanebow/services/subscription_service.dart';
import 'package:lanebow/config/api_config.dart';
import 'subscription_service_integration_test.mocks.dart';
import 'package:lanebow/utils/test_utils.dart';
import '../utils/event_bus_test_utils.dart';
// 구매 상세 정보 모킹
class MockPurchaseDetails implements PurchaseDetails {
@@ -20,21 +22,23 @@ class MockPurchaseDetails implements PurchaseDetails {
required this.verificationData,
required PurchaseStatus status,
}) : _status = status;
@override
final String purchaseID;
@override
final String productID;
@override
final PurchaseVerificationData verificationData;
// status 구현
PurchaseStatus _status;
@override
PurchaseStatus get status => _status;
@override
set status(PurchaseStatus value) { _status = value; }
set status(PurchaseStatus value) {
_status = value;
}
@override
IAPError? error;
@override
@@ -59,111 +63,121 @@ class MockPurchaseVerificationData implements PurchaseVerificationData {
class CustomMockInAppPurchaseService implements InAppPurchaseService {
// 내부 상태 변수
List<ProductDetails> _products = [];
List<PurchaseDetails> _purchases = [];
final List<PurchaseDetails> _purchases = [];
bool _isAvailable = true;
bool _isLoading = false;
String? _error;
bool _isPurchaseVerified = false;
PurchaseDetails? _lastVerifiedPurchase;
// 필수 속성 구현 - 게터
@override
List<ProductDetails> get products => _products;
@override
List<PurchaseDetails> get purchases => _purchases;
@override
bool get isAvailable => _isAvailable;
@override
bool get isLoading => _isLoading;
@override
String? get error => _error;
@override
bool get isPurchaseVerified => _isPurchaseVerified;
@override
PurchaseDetails? get lastVerifiedPurchase => _lastVerifiedPurchase;
// 필수 속성 구현 - 세터
@override
set isPurchaseVerified(bool value) => _isPurchaseVerified = value;
@override
set lastVerifiedPurchase(PurchaseDetails? value) {
_lastVerifiedPurchase = value;
if (value != null && !_purchases.contains(value)) {
_purchases.add(value);
}
}
// 필수 메서드 구현
@override
Future<bool> initialize() async {
_isLoading = true;
notifyListeners();
_isAvailable = true;
_isLoading = false;
notifyListeners();
return true;
}
@override
Future<bool> purchaseSubscription(SubscriptionPlanType planType) async {
return true;
}
@override
Future<bool> restorePurchases() async {
return true;
}
@override
ProductDetails? getProductByPlanType(SubscriptionPlanType planType) {
// 테스트용 간단 구현
return _products.isNotEmpty ? _products.first : null;
}
@override
void dispose() {
// 아무것도 하지 않음 - 테스트용
}
// ChangeNotifier 구현
final List<VoidCallback> _listeners = [];
@override
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
@override
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
}
@override
void notifyListeners() {
for (final listener in _listeners) {
listener();
}
}
@override
bool get hasListeners => _listeners.isNotEmpty;
// 테스트용 설정 메서드
void setIsPurchaseVerified(bool value) {
_isPurchaseVerified = value;
}
void setLastVerifiedPurchase(PurchaseDetails? purchase) {
_lastVerifiedPurchase = purchase;
}
void setProducts(List<ProductDetails> products) {
_products = products;
}
void setError(String? error) {
_error = error;
}
void setIsAvailable(bool value) {
_isAvailable = value;
}
@@ -190,94 +204,131 @@ void main() {
'status': 'active',
'startDate': DateTime.now().toIso8601String(),
'endDate': DateTime.now().add(Duration(days: 30)).toIso8601String(),
'price': 19900, // double이 아닌 int로 변경 (toDouble() 메서드 호출 문제 해결)
'price': 19900, // double이 아닌 int로 변경 (toDouble() 메서드 호출 문제 해결)
'currency': 'KRW',
'autoRenew': true,
'features': {
'maxMembers': 100,
'maxEvents': 100,
},
'features': {'maxMembers': 100, 'maxEvents': 100},
'paymentMethod': 'card',
'transactionId': 'test_transaction_id',
'createdAt': DateTime.now().subtract(Duration(days: 10)).toIso8601String(),
'createdAt': DateTime.now()
.subtract(Duration(days: 10))
.toIso8601String(),
'updatedAt': DateTime.now().toIso8601String(),
};
// 테스트 구독 플랜 데이터
// 테스트 구독 플랜 데이터 (SubscriptionPlan.fromJson 스키마에 맞춤)
final testSubscriptionPlans = [
{
'id': 'basic_monthly',
'name': '베이직 월간',
'planType': 'basic',
'isYearly': false,
'price': 9900,
'type': 'basic',
'name': 'Basic',
'description': 'Features for small to mid clubs',
'monthlyPrice': 9900,
'yearlyPrice': 99000,
'currency': 'KRW',
'features': {
'maxMembers': 50,
'maxEvents': 50,
'hasAdvancedStats': false,
},
'features': [
'Manage up to 50 members',
'Basic score records',
'Simple stats',
],
'maxMembers': 50,
'maxEvents': 50,
'hasAdvancedStats': false,
'hasCustomization': false,
'hasPriority': false,
},
{
'id': 'basic_yearly',
'name': '베이직 연간',
'planType': 'basic',
'isYearly': true,
'price': 99000,
'type': 'basic',
'name': 'Basic (Yearly)',
'description': 'Yearly plan for small to mid clubs',
'monthlyPrice': 9900, // monthly equivalent
'yearlyPrice': 99000,
'currency': 'KRW',
'features': {
'maxMembers': 50,
'maxEvents': 50,
'hasAdvancedStats': false,
},
'features': [
'Manage up to 50 members',
'Basic score records',
'Simple stats',
],
'maxMembers': 50,
'maxEvents': 50,
'hasAdvancedStats': false,
'hasCustomization': false,
'hasPriority': false,
},
{
'id': 'premium_monthly',
'name': '프리미엄 월간',
'planType': 'premium',
'isYearly': false,
'price': 19900,
'type': 'premium',
'name': 'Premium',
'description': 'Advanced features for large clubs',
'monthlyPrice': 19900,
'yearlyPrice': 199000,
'currency': 'KRW',
'features': {
'maxMembers': 100,
'maxEvents': 100,
'hasAdvancedStats': true,
},
'features': [
'Unlimited members',
'Advanced analytics',
'Unlimited events',
],
'maxMembers': 100,
'maxEvents': 100,
'hasAdvancedStats': true,
'hasCustomization': true,
'hasPriority': true,
},
{
'id': 'premium_yearly',
'name': '프리미엄 연간',
'planType': 'premium',
'isYearly': true,
'price': 199000,
'type': 'premium',
'name': 'Premium (Yearly)',
'description': 'Yearly advanced features for large clubs',
'monthlyPrice': 19900, // monthly equivalent
'yearlyPrice': 199000,
'currency': 'KRW',
'features': {
'maxMembers': 100,
'maxEvents': 100,
'hasAdvancedStats': true,
},
'features': [
'Unlimited members',
'Advanced analytics',
'Unlimited events',
],
'maxMembers': 100,
'maxEvents': 100,
'hasAdvancedStats': true,
'hasCustomization': true,
'hasPriority': true,
},
];
final testSubscriptionJson = jsonEncode(testSubscription);
final testSubscriptionPlansJson = jsonEncode(testSubscriptionPlans);
setUp(() {
setUp(() async {
// EventBus 격리 설정
final testId =
'subscription_service_integration_test_fixed3_${DateTime.now().millisecondsSinceEpoch}';
TestUtils.setTestMode(true);
await EventBusTestUtils.setUp(testId);
mockClient = MockClient();
mockPurchaseService = CustomMockInAppPurchaseService();
// 현재 구독 정보 API 응답 설정 - 실제 API 엔드포인트 사용
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
any,
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((invocation) async {
final uri = invocation.positionalArguments.first as Uri;
if (uri.toString().endsWith(ApiConfig.currentSubscription)) {
return http.Response(testSubscriptionJson, 200);
}
return http.Response(testSubscriptionJson, 200);
});
// 구독 플랜 목록 API 응답 설정 - 실제 API 엔드포인트 사용
when(mockClient.get(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.subscriptionPlans}'),
headers: anyNamed('headers'),
)).thenAnswer((_) async => http.Response(testSubscriptionPlansJson, 200));
when(mockClient.get(any, headers: anyNamed('headers'))).thenAnswer((
invocation,
) async {
final uri = invocation.positionalArguments.first as Uri;
if (uri.toString().contains(ApiConfig.subscriptionPlans)) {
return http.Response(testSubscriptionPlansJson, 200);
}
return http.Response(testSubscriptionJson, 200);
});
// 인앱 구매 서비스 초기화 설정 - when() 대신 직접 메서드 호출
// CustomMockInAppPurchaseService는 직접 구현된 클래스이므로 when() 대신 직접 설정 사용
@@ -293,6 +344,11 @@ void main() {
);
});
tearDown(() async {
await EventBusTestUtils.tearDown();
TestUtils.setTestMode(false);
});
test('현재 구독 정보 로드 테스트', () async {
// given
// 이미 setUp에서 현재 구독 정보 API 응답이 설정되어 있음
@@ -303,89 +359,114 @@ void main() {
// then
// loadCurrentSubscription의 반환값을 사용하지 않고 상태를 확인
expect(subscriptionService.currentSubscription, isNotNull);
expect(subscriptionService.currentSubscription!.id, 'test_subscription_id');
expect(
subscriptionService.currentSubscription!.id,
'test_subscription_id',
);
expect(subscriptionService.isLoading, isFalse);
expect(subscriptionService.error, isNull);
verify(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).called(1);
verify(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).called(1);
});
test('구독 플랜 목록 로드 테스트', () async {
// given
// 이미 setUp에서 구독 플랜 목록 API 응답이 설정되어 있음
// when
// 서비스 생성될 때 _loadAvailablePlans()가 호출되어 이미 플랜 목록이 로드되어 있음
// 서비스 생성 _loadAvailablePlans()가 비동기로 호출되므로 완료를 대기
const timeout = Duration(seconds: 2);
final start = DateTime.now();
while (subscriptionService.availablePlans.isEmpty &&
DateTime.now().difference(start) < timeout) {
await Future.delayed(const Duration(milliseconds: 20));
}
// then
expect(subscriptionService.availablePlans, isNotEmpty);
// 테스트 플랜 수와 일치하는지 확인 (실제 플랜 수가 4개로 확인됨)
// verify를 사용하여 HTTP 요청 확인 - 이미 호출되었으므로 called(1)이 아닌 called(greaterThanOrEqualTo(1)) 사용
verify(mockClient.get(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.subscriptionPlans}'),
headers: anyNamed('headers'),
)).called(greaterThanOrEqualTo(1));
verify(
mockClient.get(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.subscriptionPlans}'),
headers: anyNamed('headers'),
),
).called(greaterThanOrEqualTo(1));
});
test('구독 복원 테스트', () async {
// given
// when() 대신 직접 메서드 설정
// mockPurchaseService.restorePurchases()는 이미 true를 반환하도록 구현되어 있음
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
// when
final result = await subscriptionService.restoreSubscriptions();
// then
expect(result, isTrue);
expect(subscriptionService.isLoading, isFalse);
});
test('구독 상태 검증 테스트', () async {
// given
// 현재 구독 정보 설정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
await subscriptionService.loadCurrentSubscription();
// SubscriptionService 내부 구현에 맞게 목 설정
// verifySubscription 메서드는 GET 요청을 사용하여 구독 상태를 확인
// 실제 서비스 코드에서 사용하는 경로로 수정 - 중복 경로 제거
when(mockClient.get(
Uri.parse('${ApiConfig.baseUrl}/api/subscriptions/current'),
headers: anyNamed('headers'),
)).thenAnswer((_) async => http.Response(jsonEncode(testSubscription), 200));
when(
mockClient.get(
Uri.parse('${ApiConfig.baseUrl}/api/subscriptions/current'),
headers: anyNamed('headers'),
),
).thenAnswer(
(_) async => http.Response(jsonEncode(testSubscription), 200),
);
// 실제 서비스 코드에서 사용하는 정확한 경로 추가
when(mockClient.get(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
)).thenAnswer((_) async => http.Response(jsonEncode(testSubscription), 200));
when(
mockClient.get(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
),
).thenAnswer(
(_) async => http.Response(jsonEncode(testSubscription), 200),
);
// when
final result = await subscriptionService.verifySubscription();
// then
print('구독 상태 검증 테스트 결과: $result');
print('구독 상태 검증 테스트 오류: ${subscriptionService.error}');
expect(result, isTrue);
expect(subscriptionService.error, isNull);
});
test('새 구독 생성 테스트', () async {
// given
// 인앱 구매 서비스 목 설정 - when() 대신 직접 설정
@@ -394,32 +475,38 @@ void main() {
MockPurchaseDetails(
purchaseID: 'test_purchase_id',
productID: 'test_product_id',
verificationData: MockPurchaseVerificationData('test_verification_data'),
verificationData: MockPurchaseVerificationData(
'test_verification_data',
),
status: PurchaseStatus.purchased,
),
);
// API 응답 설정 - 실제 서비스 코드에서 사용하는 경로로 수정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}/api/subscriptions/create'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}/api/subscriptions/create'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
// 실제 서비스 코드에서 사용하는 정확한 경로 추가
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.subscribeClub}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.subscribeClub}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
// when
// createSubscription 메서드는 planType과 isYearly 두 개의 매개변수가 필요함
final result = await subscriptionService.createSubscription(
SubscriptionPlanType.premium,
false, // isYearly 매개변수 추가
);
// then
print('새 구독 생성 테스트 결과: $result');
print('새 구독 생성 테스트 오류: ${subscriptionService.error}');
@@ -427,111 +514,133 @@ void main() {
expect(subscriptionService.currentSubscription, isNotNull);
expect(subscriptionService.error, isNull);
});
test('구독 취소 테스트', () async {
// given
// 현재 구독 정보 설정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
await subscriptionService.loadCurrentSubscription();
// 구독 취소 API 응답 설정 - 실제 서비스 코드에서 사용하는 경로로 수정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}/api/subscriptions/test_subscription_id/cancel'),
headers: anyNamed('headers'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse(
'${ApiConfig.baseUrl}/api/subscriptions/test_subscription_id/cancel',
),
headers: anyNamed('headers'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
// 실제 서비스 코드에서 사용하는 정확한 경로 추가
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.subscriptions}/test_subscription_id/cancel'),
headers: anyNamed('headers'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse(
'${ApiConfig.baseUrl}${ApiConfig.subscriptions}/test_subscription_id/cancel',
),
headers: anyNamed('headers'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
// when
final result = await subscriptionService.cancelSubscription();
// then
print('구독 취소 테스트 결과: $result');
print('구독 취소 테스트 오류: ${subscriptionService.error}');
expect(result, isTrue);
expect(subscriptionService.error, isNull);
});
test('구독 플랜 변경 테스트', () async {
// given
// 현재 구독 정보 설정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
await subscriptionService.loadCurrentSubscription();
// 플랜 변경 API 응답 설정 - 실제 서비스 코드에서 사용하는 경로로 수정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}/api/subscriptions/change-plan'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}/api/subscriptions/change-plan'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
// 실제 서비스 코드에서 사용하는 정확한 경로 추가 (ApiConfig.changePlan 사용)
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.changePlan}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.changePlan}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
// 인앱 구매 서비스 목 설정 - when() 대신 직접 설정
mockPurchaseService.setIsPurchaseVerified(true);
// when
// changePlan 메서드는 newPlanType과 isYearly 두 개의 매개변수가 필요함
final result = await subscriptionService.changePlan(
SubscriptionPlanType.premium,
false, // isYearly 매개변수 추가
);
// then
print('구독 플랜 변경 테스트 결과: $result');
print('구독 플랜 변경 테스트 오류: ${subscriptionService.error}');
expect(result, isTrue);
expect(subscriptionService.error, isNull);
});
test('자동 갱신 설정 변경 테스트', () async {
// given
// 현재 구독 정보 설정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
await subscriptionService.loadCurrentSubscription();
// 자동 갱신 설정 변경 API 응답 설정 - 실제 서비스 코드에서 사용하는 경로로 수정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}/api/subscriptions/autorenew'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}/api/subscriptions/autorenew'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
// 실제 서비스 코드에서 사용하는 정확한 경로 추가
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.subscriptions}/autorenew'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.subscriptions}/autorenew'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer((_) async => http.Response(testSubscriptionJson, 200));
// when
// setAutoRenew 대신 toggleAutoRenew 메서드 사용
final result = await subscriptionService.toggleAutoRenew();
// then
print('자동 갱신 설정 변경 테스트 결과: $result');
print('자동 갱신 설정 변경 테스트 오류: ${subscriptionService.error}');
@@ -539,24 +648,30 @@ void main() {
expect(subscriptionService.error, isNull);
});
});
group('에러 처리 테스트', () {
late SubscriptionService subscriptionService;
late MockClient mockClient;
late CustomMockInAppPurchaseService mockPurchaseService;
// 테스트 사용자 ID
const testUserId = 'test_user_id';
const testClubId = 'test_club_id';
const testToken = 'test_token';
setUp(() {
setUp(() async {
// EventBus 격리 설정
final testId =
'subscription_service_integration_test_fixed3_${DateTime.now().millisecondsSinceEpoch}_error_group';
TestUtils.setTestMode(true);
await EventBusTestUtils.setUp(testId);
mockClient = MockClient();
mockPurchaseService = CustomMockInAppPurchaseService();
// 인앱 구매 서비스 초기화 설정 - when() 대신 직접 메서드 호출
mockPurchaseService.initialize(); // 직접 호출
// 구독 서비스 생성
subscriptionService = SubscriptionService.forTest(
client: mockClient,
@@ -566,81 +681,102 @@ void main() {
clubId: testClubId,
);
});
tearDown(() async {
await EventBusTestUtils.tearDown();
TestUtils.setTestMode(false);
});
test('구독 없는 상태에서 구독 취소 시도 테스트', () async {
// given
// 현재 구독 정보가 없는 상태 설정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response('{"error": "Subscription not found"}', 404));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer(
(_) async => http.Response('{"error": "Subscription not found"}', 404),
);
await subscriptionService.loadCurrentSubscription();
// when
final result = await subscriptionService.cancelSubscription();
// then
expect(result, isFalse);
expect(subscriptionService.error, isNotNull);
});
test('구독 없는 상태에서 플랜 변경 시도 테스트', () async {
// given
// 현재 구독 정보가 없는 상태 설정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response('{"error": "Subscription not found"}', 404));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer(
(_) async => http.Response('{"error": "Subscription not found"}', 404),
);
await subscriptionService.loadCurrentSubscription();
// when
// changePlan 메서드는 newPlanType과 isYearly 두 개의 매개변수가 필요함
final result = await subscriptionService.changePlan(
SubscriptionPlanType.premium,
false, // isYearly 매개변수 추가
);
// then
expect(result, isFalse);
expect(subscriptionService.error, isNotNull);
});
test('구독 없는 상태에서 자동 갱신 설정 변경 시도 테스트', () async {
// given
// 현재 구독 정보가 없는 상태 설정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response('{"error": "Subscription not found"}', 404));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer(
(_) async => http.Response('{"error": "Subscription not found"}', 404),
);
await subscriptionService.loadCurrentSubscription();
// when
// setAutoRenew 대신 toggleAutoRenew 메서드 사용
final result = await subscriptionService.toggleAutoRenew();
// then
expect(result, isFalse);
expect(subscriptionService.error, isNotNull);
});
test('구독 정보 로드 실패 테스트', () async {
// given
// 현재 구독 정보 로드 실패 설정
when(mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
)).thenAnswer((_) async => http.Response('{"error": "Server error"}', 500));
when(
mockClient.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.currentSubscription}'),
headers: anyNamed('headers'),
body: anyNamed('body'),
),
).thenAnswer(
(_) async => http.Response('{"error": "Server error"}', 500),
);
// when
await subscriptionService.loadCurrentSubscription();
// then
// 404가 아닌 다른 오류 코드에서는 currentSubscription이 null이어야 함
expect(subscriptionService.currentSubscription, isNull);