feat(payments): phase 3 test added

This commit is contained in:
2026-07-09 16:21:53 +03:00
parent 51397003b5
commit a0edfe942b
7 changed files with 312 additions and 1 deletions

View File

@@ -0,0 +1,118 @@
import 'package:eatvio_app/features/payment/repositories/sbp_repository.dart';
import 'package:eatvio_app/features/payment/services/sbp_service.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:sbp/models/c2bmembers_model.dart';
import 'sbp_repository_test.mocks.dart';
@GenerateNiceMocks([MockSpec<SbpService>()])
void main() {
group('SbpRepository', () {
late SbpRepository sbpRepository;
late MockSbpService mockSbpService;
final installedBank = C2bmemberModel.fromJson(const {
'bankName': 'Сбербанк',
'logoURL': 'https://qr.nspk.ru/proxyapp/logo/bank100000000111.png',
'schema': 'bank100000000111',
'package_name': 'ru.sberbankmobile',
'icon': 'packages/sbp/assets/icons/sber.png',
});
setUp(() {
mockSbpService = MockSbpService();
sbpRepository = SbpRepository(sbpService: mockSbpService);
});
group('getInstalledBanks', () {
test(
'делегирует сервису и возвращает список установленных банков',
() async {
when(
mockSbpService.getInstalledBanks(),
).thenAnswer((_) async => [installedBank]);
final result = await sbpRepository.getInstalledBanks();
expect(result, [installedBank]);
verify(mockSbpService.getInstalledBanks()).called(1);
},
);
test(
'возвращает пустой список, когда сервис вернул пустой список',
() async {
when(mockSbpService.getInstalledBanks()).thenAnswer((_) async => []);
final result = await sbpRepository.getInstalledBanks();
expect(result, isEmpty);
},
);
test(
'бросает exception при ошибке нативного плагина (graceful)',
() async {
when(
mockSbpService.getInstalledBanks(),
).thenThrow(MissingPluginException('no sbp handler'));
expect(() => sbpRepository.getInstalledBanks(), throwsException);
},
);
});
group('openBank', () {
const checkoutUrl = 'https://qr.nspk.ru/ASDFASDFASDFASDF';
test(
'делегирует сервису и возвращает true при успешном запуске',
() async {
when(
mockSbpService.openBank(url: checkoutUrl, bank: installedBank),
).thenAnswer((_) async => true);
final result = await sbpRepository.openBank(
url: checkoutUrl,
bank: installedBank,
);
expect(result, isTrue);
verify(
mockSbpService.openBank(url: checkoutUrl, bank: installedBank),
).called(1);
},
);
test('возвращает false, когда сервис вернул false', () async {
when(
mockSbpService.openBank(url: checkoutUrl, bank: installedBank),
).thenAnswer((_) async => false);
final result = await sbpRepository.openBank(
url: checkoutUrl,
bank: installedBank,
);
expect(result, isFalse);
});
test(
'возвращает false при ошибке нативного плагина (graceful)',
() async {
when(
mockSbpService.openBank(url: checkoutUrl, bank: installedBank),
).thenThrow(MissingPluginException('no sbp handler'));
expect(
() => sbpRepository.openBank(url: checkoutUrl, bank: installedBank),
throwsException,
);
},
);
});
});
}

View File

@@ -0,0 +1,119 @@
import 'package:eatvio_app/features/payment/services/sbp_service.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sbp/models/c2bmembers_model.dart';
/// Тесты реализации [SbpServiceImpl] — обёртки над статичным классом [Sbp].
///
/// Поскольку unit-тесты исполняются на хост-машине (не iOS), срабатывает
/// Android-ветка плагина: `getInstalledBanks` возвращает список банков через
/// `package_name`, а `openBank` подставляет схему банка в начало checkout_url.
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
const channel = MethodChannel('sbp');
/// Банк-фикстура: Сбербанк (присутствует в справочнике c2bmembersData пакета).
final sberbank = C2bmemberModel.fromJson(const {
'bankName': 'Сбербанк',
'logoURL': 'https://qr.nspk.ru/proxyapp/logo/bank100000000111.png',
'schema': 'bank100000000111',
'package_name': 'ru.sberbankmobile',
'icon': 'packages/sbp/assets/icons/sber.png',
});
late SbpService sbpService;
setUp(() {
sbpService = const SbpServiceImpl();
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, null);
});
group('SbpServiceImpl.getInstalledBanks', () {
test('возвращает список банков, установленных на устройстве (Android)', () async {
// Плагин (Android) возвращает список установленных банков в формате
// {name, bitmap, package_name}; обёртка сопоставляет со справочником.
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall call) async {
return [
{'name': 'Сбербанк', 'bitmap': null, 'package_name': 'ru.sberbankmobile'},
];
});
final result = await sbpService.getInstalledBanks();
expect(result, isNotEmpty);
expect(
result.any(
(bank) =>
bank.bankName == 'Сбербанк' && bank.packageName == 'ru.sberbankmobile',
),
isTrue,
);
});
test('возвращает пустой список, если нет установленных банков', () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall call) async {
return <Map<String, dynamic>>[];
});
final result = await sbpService.getInstalledBanks();
expect(result, isEmpty);
});
test('пробрасывает ошибку плагина при отсутствии нативного обработчика', () async {
// Обработчик не зарегистрирован → invokeMethod выбрасывает
// MissingPluginException. Сервис должен пробросить её наверх.
expect(
sbpService.getInstalledBanks(),
throwsA(isA<MissingPluginException>()),
);
});
});
group('SbpServiceImpl.openBank', () {
test('вызывает нативный openBank с подстановкой схемы банка в url и возвращает true', () async {
const checkoutUrl = 'https://qr.nspk.ru/ASDFASDFASDFASDF';
String? receivedUrl;
String? receivedPackageName;
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall call) async {
if (call.method == 'openBank') {
final args = call.arguments as Map<dynamic, dynamic>;
receivedUrl = args['url'] as String?;
receivedPackageName = args['package_name'] as String?;
return true;
}
return null;
});
final result = await sbpService.openBank(url: checkoutUrl, bank: sberbank);
expect(result, isTrue);
// Android-ветка заменяет схему "https" на schema банка в начале url.
expect(receivedUrl, 'bank100000000111://qr.nspk.ru/ASDFASDFASDFASDF');
expect(receivedPackageName, 'ru.sberbankmobile');
});
test('возвращает false при неудачном запуске банковского приложения', () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall call) async {
return false;
});
final result = await sbpService.openBank(
url: 'https://qr.nspk.ru/ASDFASDFASDFASDF',
bank: sberbank,
);
expect(result, isFalse);
});
});
}