[{"data":1,"prerenderedAt":75},["ShallowReactive",2],{"tutorial-test-unitari-in-flutter-con-mockito-e-mocktail-mockare-le-dipendenze":3,"comments-tutorial-test-unitari-in-flutter-con-mockito-e-mocktail-mockare-le-dipendenze":74},{"id":4,"title":5,"slug":6,"excerpt":7,"intro":8,"cover_image":9,"video_url":10,"difficulty":11,"estimated_minutes":12,"flutter_version":13,"status":14,"published_at":15,"meta_title":16,"meta_description":17,"category":18,"author":22,"steps":25},43,"Test unitari in Flutter con mockito e mocktail: mockare le dipendenze","test-unitari-in-flutter-con-mockito-e-mocktail-mockare-le-dipendenze","Impara a scrivere test unitari robusti in Flutter isolando le dipendenze con i mock. Confronto pratico tra mockito e mocktail per testare repository e servizi.","# Test unitari con i mock in Flutter\n\nQuando testiamo la logica di business (repository, service, controller), non vogliamo dipendere da chiamate HTTP reali o da database. La soluzione è **mockare** le dipendenze: sostituirle con oggetti finti che restituiscono valori controllati.\n\nIn questo tutorial vedremo come scrivere test unitari isolati usando due librerie molto diffuse:\n\n- **mockito**: la libreria storica, basata su generazione di codice con `build_runner`.\n- **mocktail**: alternativa moderna, senza generazione di codice, con una sintassi più snella.\n\nAlla fine avrai un `UserRepository` testato al 100% senza mai toccare la rete.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F9dc78fe1-a670-46e4-b7ce-a85abc39f2bb.jpg",null,"intermediate",35,"3.x","published","2026-07-20T04:30:47+00:00","Test unitari Flutter con mockito e mocktail","Guida pratica ai test unitari in Flutter: mocka le dipendenze con mockito e mocktail per testare repository e servizi in modo isolato.",{"id":19,"name":20,"slug":21},3,"Best practice","best-practice",{"id":23,"name":24},1,"Flutter Bot",[26,33,41,47,54,61,68],{"id":27,"position":23,"title":28,"body":29,"code_snippet":30,"code_language":31,"expected_result":32,"demo_url":10,"video_url":10},289,"Aggiungere le dipendenze di test","Aggiungiamo le librerie necessarie nella sezione `dev_dependencies` del `pubspec.yaml`. Includiamo sia **mockito** (che richiede `build_runner`) sia **mocktail**, così potrai confrontare i due approcci. Il pacchetto `http` ci serve come dipendenza da mockare.","dependencies:\n  http: ^1.2.0\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n  mockito: ^5.4.4\n  build_runner: ^2.4.9\n  mocktail: ^1.0.3","yaml","Dopo aver salvato, esegui `flutter pub get` senza errori.",{"id":34,"position":35,"title":36,"body":37,"code_snippet":38,"code_language":39,"expected_result":40,"demo_url":10,"video_url":10},290,2,"Creare la classe da testare","Definiamo un semplice `UserRepository` che dipende da un `http.Client`. Iniettare la dipendenza dal costruttore è fondamentale: ci permette di passare un client finto durante i test.","import 'dart:convert';\nimport 'package:http\u002Fhttp.dart' as http;\n\nclass User {\n  final int id;\n  final String name;\n  User({required this.id, required this.name});\n\n  factory User.fromJson(Map\u003CString, dynamic> json) =>\n      User(id: json['id'] as int, name: json['name'] as String);\n}\n\nclass UserRepository {\n  final http.Client client;\n  UserRepository(this.client);\n\n  Future\u003CUser> fetchUser(int id) async {\n    final response = await client.get(\n      Uri.parse('https:\u002F\u002Fapi.example.com\u002Fusers\u002F$id'),\n    );\n    if (response.statusCode == 200) {\n      return User.fromJson(jsonDecode(response.body) as Map\u003CString, dynamic>);\n    }\n    throw Exception('Errore nel caricamento utente');\n  }\n}","dart","Il file `lib\u002Fuser_repository.dart` compila correttamente.",{"id":42,"position":19,"title":43,"body":44,"code_snippet":45,"code_language":39,"expected_result":46,"demo_url":10,"video_url":10},291,"Generare i mock con mockito","Con mockito dichiariamo quali classi mockare tramite l'annotazione `@GenerateMocks`, poi eseguiamo `build_runner` per generare il codice. Crea il file `test\u002Fuser_repository_test.dart`.\n\nEsegui in terminale:\n\n```bash\nflutter pub run build_runner build --delete-conflicting-outputs\n```\n\nQuesto genera il file `user_repository_test.mocks.dart` con la classe `MockClient`.","import 'package:flutter_test\u002Fflutter_test.dart';\nimport 'package:http\u002Fhttp.dart' as http;\nimport 'package:mockito\u002Fannotations.dart';\n\nimport 'user_repository_test.mocks.dart';\n\n@GenerateMocks([http.Client])\nvoid main() {\n  \u002F\u002F I test verranno aggiunti nel prossimo passo\n}","Viene creato il file `user_repository_test.mocks.dart` senza errori di build.",{"id":48,"position":49,"title":50,"body":51,"code_snippet":52,"code_language":39,"expected_result":53,"demo_url":10,"video_url":10},292,4,"Scrivere i test con mockito","Ora usiamo `when(...).thenAnswer(...)` per definire il comportamento del mock e `verify(...)` per controllare che il metodo sia stato chiamato. Testiamo sia il caso di successo sia il caso di errore.","import 'package:flutter_test\u002Fflutter_test.dart';\nimport 'package:http\u002Fhttp.dart' as http;\nimport 'package:mockito\u002Fannotations.dart';\nimport 'package:mockito\u002Fmockito.dart';\n\nimport '..\u002Flib\u002Fuser_repository.dart';\nimport 'user_repository_test.mocks.dart';\n\n@GenerateMocks([http.Client])\nvoid main() {\n  late MockClient client;\n  late UserRepository repository;\n\n  setUp(() {\n    client = MockClient();\n    repository = UserRepository(client);\n  });\n\n  test('restituisce un User quando la risposta è 200', () async {\n    when(client.get(any)).thenAnswer(\n      (_) async => http.Response('{\"id\": 1, \"name\": \"Mario\"}', 200),\n    );\n\n    final user = await repository.fetchUser(1);\n\n    expect(user.name, 'Mario');\n    verify(client.get(any)).called(1);\n  });\n\n  test('lancia un\\'eccezione quando la risposta non è 200', () async {\n    when(client.get(any))\n        .thenAnswer((_) async => http.Response('Not found', 404));\n\n    expect(() => repository.fetchUser(1), throwsException);\n  });\n}","Eseguendo `flutter test` entrambi i test passano (verde).",{"id":55,"position":56,"title":57,"body":58,"code_snippet":59,"code_language":39,"expected_result":60,"demo_url":10,"video_url":10},293,5,"L'alternativa senza codegen: mocktail","Con **mocktail** non serve `build_runner`. Basta creare una classe che estende `Mock` e implementa l'interfaccia. Un aspetto importante: mocktail richiede di registrare i *fallback value* per i tipi custom usati con `any()`, tramite `registerFallbackValue`. Nota anche l'uso di `any()` come funzione anziché `any`.","import 'package:flutter_test\u002Fflutter_test.dart';\nimport 'package:http\u002Fhttp.dart' as http;\nimport 'package:mocktail\u002Fmocktail.dart';\n\nimport '..\u002Flib\u002Fuser_repository.dart';\n\nclass MockClient extends Mock implements http.Client {}\n\nclass FakeUri extends Fake implements Uri {}\n\nvoid main() {\n  late MockClient client;\n  late UserRepository repository;\n\n  setUpAll(() {\n    registerFallbackValue(FakeUri());\n  });\n\n  setUp(() {\n    client = MockClient();\n    repository = UserRepository(client);\n  });\n\n  test('restituisce un User quando la risposta è 200', () async {\n    when(() => client.get(any())).thenAnswer(\n      (_) async => http.Response('{\"id\": 2, \"name\": \"Luigi\"}', 200),\n    );\n\n    final user = await repository.fetchUser(2);\n\n    expect(user.name, 'Luigi');\n    verify(() => client.get(any())).called(1);\n  });\n}","Il test con mocktail passa senza aver eseguito alcuna generazione di codice.",{"id":62,"position":63,"title":64,"body":65,"code_snippet":66,"code_language":39,"expected_result":67,"demo_url":10,"video_url":10},294,6,"Verificare argomenti e ordine delle chiamate","I mock non servono solo a restituire valori finti: permettono di **verificare le interazioni**. Possiamo controllare gli argomenti esatti con `captureAny`\u002F`captured`, l'ordine con `verifyInOrder`, e assicurarci che non ci siano chiamate impreviste con `verifyNoMoreInteractions`. Questo esempio usa mocktail.","test('chiama l\\'URL corretto con l\\'id dell\\'utente', () async {\n  when(() => client.get(any())).thenAnswer(\n    (_) async => http.Response('{\"id\": 5, \"name\": \"Anna\"}', 200),\n  );\n\n  await repository.fetchUser(5);\n\n  final captured = verify(() => client.get(captureAny())).captured;\n  final uri = captured.single as Uri;\n\n  expect(uri.path, '\u002Fusers\u002F5');\n  verifyNoMoreInteractions(client);\n});","Il test conferma che il repository costruisce l'URL corretto e non effettua chiamate extra.",{"id":69,"position":70,"title":71,"body":72,"code_snippet":10,"code_language":39,"expected_result":73,"demo_url":10,"video_url":10},295,7,"Best practice e quando usare cosa","Alcune regole per test manutenibili:\n\n- **Inietta sempre le dipendenze** dal costruttore: senza dependency injection non puoi mockare.\n- **Un mock per test**: usa `setUp` per ricreare i mock, evitando stato condiviso tra test.\n- **Testa i comportamenti, non l'implementazione**: verifica input\u002Foutput, evita di verificare ogni singola chiamata interna se non necessario.\n- **mockito vs mocktail**:\n  - Scegli **mocktail** per progetti nuovi: niente `build_runner`, sintassi più pulita, supporto per null safety immediato.\n  - Scegli **mockito** se il progetto lo usa già o se preferisci mock generati e type-safe.\n\nEsegui l'intera suite con:\n\n```bash\nflutter test --coverage\n```\n\ne genera un report di copertura con `genhtml coverage\u002Flcov.info -o coverage\u002Fhtml`.","Hai una suite di test unitari isolata, veloce e ripetibile, con la copertura del tuo repository misurabile.",[],1785219598335]