Testēšana — CA Navigator¶
Šis dokuments apraksta testu komplektu, kā to palaist un pievienot jaunus testus.
Pašreizējais stāvoklis: 580 testi visi iziet (485 unit + 95 integration).
Saturs¶
- Ātrs sākums
- Testu kategorijas
- Testu failu organizācija
- Pārklājums pa servisiem
- Atklāti reāli bugi
- Kā rakstīt jaunus testus
- Test infrastruktūra
- Kas nav ietverts un kāpēc
- Test fixture atjaunināšana
- Atsevišķs
geo-clip-testsrepo
Ātrs sākums¶
cd geo-mobile
# Visi unit testi (~40s, bez tīkla)
flutter test test/ --exclude-tags integration
# Tikai integration testi (~1-2 min, prasa tīklu)
flutter test test/ --tags integration
# Visi testi
flutter test test/
# Konkrēts modulis
flutter test test/services/cadastre_service_test.dart --tags integration
flutter test test/services/polygon_clip_100_cases_test.dart
Windows priekšnoteikumi:
# Flutter SDK PATH-ā
$env:Path = "C:\Users\<USER>\flutter\bin;$env:Path"
# Ja lieto Flutter bundled Dart, tad `git` jābūt PATH-ā (Dart prasa engine versiju)
$env:Path = "C:\Program Files\Git\bin;$env:Path"
Testu kategorijas¶
🌐 Integration tests (@Tags(['integration'])) — 95 testi¶
Prasa tīklu un ārējo API pieejamību (VMD, VZD, LVM Geo, OSM, Open-Meteo). Atklāj reālas API kļūmes (timeout, 5xx, datu formāta izmaiņas).
| Serviss | Testi | Faili |
|---|---|---|
| CadastreService | 27 | cadastre_service_test.dart |
| VmdService | 6 | vmd_service_test.dart |
| ForestInfoService | 11 | forest_info_service_test.dart |
| MultiLayerIdentify | 10 | multi_layer_identify_test.dart |
| SearchService | 15 | search_service_test.dart |
| Heritage + Ozols | 9 | ozols_heritage_test.dart |
| LadService | 6 | lad_service_integration_test.dart |
| WeatherService | 7 | weather_service_test.dart |
| EnvironmentalData | 4 | environmental_data_service_test.dart |
Palaišana CI: izlaidiet ar --exclude-tags integration ja CI nav stabils tīkls.
🧪 Unit tests — 485 testi¶
Bez tīkla atkarības. Ātri (kopā ~40s).
Tīra loģika (~150 testi)¶
| Komponents | Testi |
|---|---|
PolygonClipService (ear-clipping triangulācija + S-H clip) |
107 |
Projections (LKS-92 ↔ WGS84 round-trip) |
17 |
GeometryKinks (poligonu pašsakrustošanās) |
13 |
IdentifyTolerance |
7 |
ServiceMetadata + zoom-from-scale |
10 |
Modeļi un serializācija (~165 testi)¶
| Komponents | Testi |
|---|---|
CaModel, DrawingModel, MapMarkerModel, SkiceModel |
~88 |
GeofenceSettings model + persistence |
11 |
SyncAction (SyncQueue) |
7 |
GeoFeature + IdentifyResult data klases |
7 |
OfflineArea + OfflineMapsService |
11 |
KmlService parse/export |
10 |
SkiceGeoJsonService + SkiceKmlService |
10 |
ForestClassifiers |
6 |
| Storage services (Skice/Drawing/CaStatus/Visibility) | 19 |
Servisi ar persistence (~115 testi)¶
| Komponents | Testi |
|---|---|
MeasurementService |
31 |
KalmanSmoother |
10 |
DeeplinkService |
11 |
ErrorMessages |
22 |
Formatters (safeFilename, Fmt) |
18 |
ConsentService |
11 |
ThemeService + OnboardingService |
10 |
FieldModeService + SavedPropertiesService |
15 |
CustomWmsService |
8 |
NewsService |
5 |
LadMetaService |
7 |
ParishForestryService |
9 |
Servisi ar mocked I/O (~13 testi)¶
| Komponents | Testi |
|---|---|
PackageService (.capack roundtrip) |
6 |
GdprService |
7 |
Widget testi (2)¶
| Komponents | Testi |
|---|---|
PulsingHalo |
1 |
MaterialApp boot sanity |
1 |
Testu failu organizācija¶
geo-mobile/test/
├── fixtures/
│ └── real_compartments.json # 80 reāli VMD nogabali (Vidzemes apkārtne)
├── models/
│ ├── ca_model_test.dart
│ ├── drawing_model_test.dart
│ ├── map_marker_model_test.dart
│ └── geofence_settings_test.dart
├── services/
│ ├── cadastre_service_test.dart [integration]
│ ├── consent_service_test.dart
│ ├── custom_wms_service_test.dart
│ ├── deeplink_service_test.dart
│ ├── environmental_data_service_test.dart [integration]
│ ├── error_messages_test.dart
│ ├── forest_classifiers_test.dart
│ ├── forest_info_service_test.dart [integration]
│ ├── forest_infra_test.dart
│ ├── gdpr_service_test.dart
│ ├── geofence_logic_test.dart
│ ├── geo_service_test.dart
│ ├── kalman_smoother_test.dart
│ ├── kml_service_test.dart
│ ├── lad_meta_service_test.dart
│ ├── lad_service_test.dart
│ ├── lad_service_integration_test.dart [integration]
│ ├── measurement_service_test.dart
│ ├── multi_layer_identify_test.dart [integration]
│ ├── news_service_test.dart
│ ├── offline_maps_service_test.dart
│ ├── ozols_heritage_test.dart [integration]
│ ├── package_service_test.dart
│ ├── parish_forestry_service_test.dart
│ ├── persistence_services_test.dart
│ ├── polygon_clip_service_test.dart
│ ├── polygon_clip_real_data_test.dart
│ ├── polygon_clip_100_cases_test.dart
│ ├── search_service_test.dart [integration]
│ ├── service_metadata_test.dart
│ ├── skice_export_services_test.dart
│ ├── storage_services_test.dart
│ ├── sync_queue_model_test.dart
│ ├── theme_onboarding_service_test.dart
│ ├── vmd_service_test.dart [integration]
│ └── weather_service_test.dart [integration]
├── utils/
│ ├── formatters_test.dart
│ ├── geometry_kinks_test.dart
│ ├── identify_tolerance_test.dart
│ └── projections_test.dart
└── widget_test.dart
Pārklājums pa servisiem¶
✅ Pilnībā pārklāti¶
PolygonClipService, MeasurementService, KalmanSmoother, Projections, GeometryKinks, DeeplinkService, ErrorMessages, Formatters, IdentifyTolerance, ConsentService, ThemeService, OnboardingService, FieldModeService, SavedPropertiesService, CustomWmsService, ForestClassifiers, NewsService, LadMetaService, ParishForestryService, ServiceMetadata, GeoService data klases.
⚠️ Daļēji pārklāti¶
- CadastreService — publiskie metodes (lookupBasic, getCompartmentsInBbox, getIntersectingCadastres, getPolygon, exists). Backend-only metodes (lookupDetails, lookupMikroliegumi) nav pārklātas — prasa VZD KIS auth.
- VmdService — error handling. Success path netiek bombēts ar fake-numuriem.
- ForestInfoService — getInfo (LVM WMS). getInfoByKadastrs ir backend-dependent.
- LadService — fallback path. Server-side path netestēts.
- PackageService — importFromFile roundtrip. exportAndShare prasa Share plugin.
- GdprService — model + flow tests. Network DELETE netestēts.
❌ Nav pārklāti (skip — platform plugins / risks)¶
| Serviss | Iemesls |
|---|---|
GeofenceService |
Geolocator + FlutterCompass platform plugins |
TrackRecordingService |
Atkarīga no GeofenceService instance |
BackgroundGpsService |
Android-specific background plugin |
MediaService |
File I/O ar permissions |
SubscriptionService |
in_app_purchase plugin (real billing risk) |
ServerService |
device_info_plus + package_info_plus |
NotificationSubscriber |
ntfy SSE protokols |
TelemetryService |
Sentry + platform |
SkicePdfService |
PDF binary, vajag platform |
SkiceShpService |
Shapefile binary, vajag platform |
AppDataMigrator |
package_info_plus |
ConnectivityService |
connectivity_plus plugin |
CachedTileProvider |
flutter_map dependent |
NavigationService |
Flutter navigator stack |
ServiceFactory |
Per-platform layer factory |
Šie ir atstājami integration_test/ ar reālas ierīces palaišanu.
Atklāti reāli bugi¶
🐛 #1: CadastreService BBOX axis order (kritisks)¶
getIntersectingCadastres, getCompartmentsInBbox u.c. izmantoja lat,lon
BBOX formātu, bet LVM Geo Server ar srsName=EPSG:4326 interpretē kā lon,lat.
Rezultāts: WFS atgrieza 0 features visās query — funkcija nestrādāja vispār.
Lietotāja redzamā simptoms: "papildus īpašnieku saraksts vienmēr tukšs".
Salabots: cadastre_service.dart:911 (un 2 citi gadījumi).
🐛 #2: getClippedCompartments ar nekonveksām skicēm (precizitāte)¶
Sutherland-Hodgman algoritms prasa, lai CLIP poligons būtu konvekss. L-veida vai U-veida skicēm tas dot pārvērtētu rezultātu (līdz 30%+ kļūda). Tā ir kritiska legāla problēma — MK 935. cirsmas apliecinājumam jārāda precīzas nogabalu daļu platības.
Salabots: polygon_clip_service.dart — pievienota
ear-clipping triangulācija. Validēts ar 107 testiem (real-data + 100 random scenāriji).
Kā rakstīt jaunus testus¶
Unit testi (bez tīkla)¶
import 'package:flutter_test/flutter_test.dart';
import 'package:ca_navigator/services/your_service.dart';
void main() {
group('YourService.someMethod', () {
test('happy path', () {
final result = YourService.someMethod('input');
expect(result, equals('expected'));
});
test('edge case: null input', () {
expect(() => YourService.someMethod(null), throwsArgumentError);
});
});
}
Integration testi (ar tīklu)¶
Pievieno @Tags(['integration']) lib augšā un setUpAll ar
ServiceConfig.loadForTest():
@Tags(['integration'])
library;
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:ca_navigator/services/your_service.dart';
import 'package:ca_navigator/services/service_config.dart';
void main() {
setUpAll(() {
final json = File('assets/config/services.json').readAsStringSync();
ServiceConfig.loadForTest(json);
});
test('reālais API atgriež datus', () async {
final result = await YourService.fetchSomething();
expect(result, isNotNull);
}, timeout: const Timeout(Duration(seconds: 30)));
}
Svarīgi: NEIZMANTOJ TestWidgetsFlutterBinding.ensureInitialized() integration
testos — tas bloķē visus HTTP pieprasījumus (atgriež 400). Lieto
ServiceConfig.loadForTest() un raw File() lasīšanu.
Tests ar mocked SharedPreferences¶
import 'package:shared_preferences/shared_preferences.dart';
void main() {
setUpAll(() {
SharedPreferences.setMockInitialValues({});
});
setUp(() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear();
});
// ...
}
Tests ar mocked path_provider¶
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
setUpAll(() {
TestWidgetsFlutterBinding.ensureInitialized();
const channel = MethodChannel('plugins.flutter.io/path_provider');
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (call) async {
return Directory.systemTemp.path;
});
});
}
Test infrastruktūra¶
ServiceConfig.loadForTest()¶
service_config.dart — speciāli testiem
pievienota statiska metode, kas inicializē config no JSON virknes (parasti
no assets/config/services.json lasītā ar File()), nevis no rootBundle.
Tas ļauj integration testiem palaisties bez Flutter binding, kas savukārt
ļauj reāli HTTP pieprasījumi (binding bloķē tos).
@visibleForTesting un invalidateCache¶
Daži servisi ir singleton ar statisku cache (CustomWmsService,
FieldModeService.instance). Testos starp test cases pievienota
@visibleForTesting static void invalidateCache() metode, lai atiestatītu cache.
Real-data fixture¶
test/fixtures/real_compartments.json
— 80 reāli VMD nogabali no Vidzemes apkārtnes, lejupielādēti no LVM Geo WFS.
Lieto polygon_clip_real_data_test.dart un polygon_clip_100_cases_test.dart.
Atjaunināšana:
cd geo-clip-tests
bash tool/fetch_fixture.sh
cp test/fixtures/real_compartments.json ../geo-mobile/test/fixtures/real_compartments.json
Test fixture atjaunināšana¶
Fixture (real_compartments.json) tiek lejupielādēts no LVM Geo WFS publiskā
endpoint-a. Tas var laiku pa laikam mainīties (jauni nogabali, izmaiņas).
# Vidzemes apkārtne (Madonas mežniecība) — 80 nogabali
curl -s "https://lvmgeoserver.lvm.lv/geoserver/publicwfs/ows?service=WFS&version=2.0.0&request=GetFeature&typeNames=publicwfs:vmdpubliccompartments&outputFormat=application/json&srsName=EPSG:4326&count=80&BBOX=25.30,56.50,25.45,56.58,EPSG:4326" \
-o test/fixtures/real_compartments.json
# Pārbauda
flutter test test/services/polygon_clip_real_data_test.dart
Atsevišķs geo-clip-tests repo¶
PolygonClipService algoritms ir kritisks (skiču platību aprēķins) un tā
107 testi ir kopēti uz neatkarīgu Dart pakotni geo-clip-tests/ (sibling
direktorijā), kas:
- Ir pure Dart (bez Flutter atkarībām)
- Var palaist neatkarīgi:
dart pub get && dart test - Ir savs git repo
- Satur arī fixture un fetch skriptu
Lieto, ja gribi: - Ātri palaist tikai poligonu testus bez Flutter setup - Iestatīt CI tikai šim algoritmam - Pārliecināties, ka algoritms ir pārvietojams uz citu projektu
cd ../geo-clip-tests
dart pub get
dart test # Visi 107 testi
dart test test/polygon_clip_100_cases_test.dart # Tikai 91 random