Pāriet uz saturu

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

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.
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