[{"data":1,"prerenderedAt":74},["ShallowReactive",2],{"tutorial-testare-i-widget-in-flutter-con-widget-test-e-flutter-test":3,"comments-tutorial-testare-i-widget-in-flutter-con-widget-test-e-flutter-test":73},{"id":4,"title":5,"slug":6,"excerpt":7,"intro":8,"cover_image":9,"video_url":10,"difficulty":11,"estimated_minutes":4,"flutter_version":12,"status":13,"published_at":14,"meta_title":15,"meta_description":16,"category":17,"author":21,"steps":24},35,"Testare i widget in Flutter con widget test e flutter_test","testare-i-widget-in-flutter-con-widget-test-e-flutter-test","Impara a scrivere widget test affidabili in Flutter: pump, finder, matcher e interazioni utente per verificare che la tua UI funzioni davvero.","## Perché testare i widget?\n\nUn'app Flutter cresce rapidamente e con essa il rischio di introdurre regressioni. I **widget test** offrono un compromesso ideale tra velocità e affidabilità: sono più veloci dei test di integrazione ma verificano davvero l'interfaccia, a differenza degli unit test puri.\n\nIn questo tutorial imparerai a usare il pacchetto `flutter_test` (incluso di default in ogni progetto) per:\n\n- montare un widget in un ambiente di test con `pumpWidget`\n- trovare elementi nella UI con i **Finder**\n- verificare lo stato con i **Matcher**\n- simulare tap, scroll e inserimento di testo\n- gestire animazioni e stati asincroni\n\nAl termine avrai una suite di test pronta da eseguire con `flutter test`.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F2669e22b-4187-4aee-b84a-42e4119d951f.jpg",null,"intermediate","3.x","published","2026-07-11T04:30:57+00:00","Widget test in Flutter con flutter_test: guida pratica","Guida pratica ai widget test in Flutter: pumpWidget, Finder, Matcher e simulazione di tap e input per verificare la tua UI con flutter_test.",{"id":18,"name":19,"slug":20},3,"Best practice","best-practice",{"id":22,"name":23},1,"Flutter Bot",[25,32,39,45,52,59,66],{"id":26,"position":22,"title":27,"body":28,"code_snippet":29,"code_language":30,"expected_result":31,"demo_url":10,"video_url":10},237,"Preparare il progetto e il widget da testare","Il pacchetto `flutter_test` è già presente in ogni progetto Flutter sotto `dev_dependencies`. Verifica il `pubspec.yaml`:\n\n```yaml\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n```\n\nCreiamo un semplice widget `Counter` che testeremo. Salvalo in `lib\u002Fcounter.dart`. È un classico contatore con un titolo, un valore e un pulsante di incremento.","import 'package:flutter\u002Fmaterial.dart';\n\nclass Counter extends StatefulWidget {\n  const Counter({super.key});\n\n  @override\n  State\u003CCounter> createState() => _CounterState();\n}\n\nclass _CounterState extends State\u003CCounter> {\n  int _value = 0;\n\n  void _increment() => setState(() => _value++);\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: [\n        const Text('Contatore'),\n        Text('$_value', key: const Key('value')),\n        ElevatedButton(\n          onPressed: _increment,\n          child: const Text('Incrementa'),\n        ),\n      ],\n    );\n  }\n}","dart","Il progetto contiene un widget Counter riutilizzabile pronto per essere testato.",{"id":33,"position":34,"title":35,"body":36,"code_snippet":37,"code_language":30,"expected_result":38,"demo_url":10,"video_url":10},238,2,"Scrivere il primo widget test con testWidgets e pumpWidget","I file di test vanno nella cartella `test\u002F` e terminano con `_test.dart`. Usiamo `testWidgets` che fornisce un `WidgetTester`, l'oggetto con cui pilotiamo il test.\n\nIl passaggio chiave è `pumpWidget`: monta il widget nell'albero di test. Poiché quasi ogni widget richiede un contesto Material (tema, direzionalità del testo, ecc.), avvolgiamo `Counter` in un `MaterialApp` con `Scaffold`.\n\nCrea `test\u002Fcounter_test.dart`.","import 'package:flutter\u002Fmaterial.dart';\nimport 'package:flutter_test\u002Fflutter_test.dart';\nimport 'package:mia_app\u002Fcounter.dart';\n\nvoid main() {\n  testWidgets('Il contatore parte da 0', (WidgetTester tester) async {\n    await tester.pumpWidget(\n      const MaterialApp(\n        home: Scaffold(body: Counter()),\n      ),\n    );\n\n    \u002F\u002F Il valore iniziale deve essere 0\n    expect(find.text('0'), findsOneWidget);\n    expect(find.text('1'), findsNothing);\n  });\n}","Eseguendo `flutter test` il test passa, confermando che il valore iniziale è 0.",{"id":40,"position":18,"title":41,"body":42,"code_snippet":43,"code_language":30,"expected_result":44,"demo_url":10,"video_url":10},239,"Trovare i widget con i Finder","I **Finder** individuano widget nell'albero. I più usati sono:\n\n- `find.text('...')` — cerca per testo visualizzato\n- `find.byKey(const Key('...'))` — cerca per Key (il modo più robusto)\n- `find.byType(ElevatedButton)` — cerca per tipo di widget\n- `find.byIcon(Icons.add)` — cerca per icona\n- `find.widgetWithText(ElevatedButton, 'Incrementa')` — combinazione tipo + testo\n\nUsare una `Key` è la scelta migliore quando più widget mostrano lo stesso testo. Nel nostro `Counter` abbiamo assegnato `Key('value')` al testo del valore.","testWidgets('I finder individuano gli elementi giusti', (tester) async {\n  await tester.pumpWidget(\n    const MaterialApp(home: Scaffold(body: Counter())),\n  );\n\n  \u002F\u002F Per Key\n  expect(find.byKey(const Key('value')), findsOneWidget);\n\n  \u002F\u002F Per tipo\n  expect(find.byType(ElevatedButton), findsOneWidget);\n\n  \u002F\u002F Per tipo + testo\n  expect(\n    find.widgetWithText(ElevatedButton, 'Incrementa'),\n    findsOneWidget,\n  );\n});","Tutti i finder localizzano correttamente gli elementi previsti nell'interfaccia.",{"id":46,"position":47,"title":48,"body":49,"code_snippet":50,"code_language":30,"expected_result":51,"demo_url":10,"video_url":10},240,4,"Verificare lo stato con i Matcher","I **Matcher** definiscono cosa ci aspettiamo da un Finder. I più comuni:\n\n- `findsOneWidget` — esattamente un widget corrispondente\n- `findsNothing` — nessun widget\n- `findsNWidgets(n)` — esattamente n widget\n- `findsWidgets` — almeno uno\n\nPuoi anche estrarre il widget con `tester.widget\u003CT>(finder)` per verificarne le proprietà interne, utile per controllare valori non visibili come `onPressed`.","testWidgets('Il pulsante ha una callback attiva', (tester) async {\n  await tester.pumpWidget(\n    const MaterialApp(home: Scaffold(body: Counter())),\n  );\n\n  final button = tester.widget\u003CElevatedButton>(\n    find.byType(ElevatedButton),\n  );\n\n  \u002F\u002F La callback non deve essere null (pulsante abilitato)\n  expect(button.onPressed, isNotNull);\n});","Il test conferma che il pulsante è abilitato perché onPressed non è null.",{"id":53,"position":54,"title":55,"body":56,"code_snippet":57,"code_language":30,"expected_result":58,"demo_url":10,"video_url":10},241,5,"Simulare le interazioni utente e ricostruire con pump","Per simulare un tap usiamo `tester.tap(finder)`. **Attenzione**: dopo un'interazione lo stato cambia ma l'albero NON viene ricostruito automaticamente. Devi chiamare `await tester.pump()` per innescare un nuovo frame e riflettere le modifiche.\n\nAltre azioni utili:\n\n- `tester.enterText(finder, 'testo')` — inserisce testo in un campo\n- `tester.drag(finder, offset)` — trascina\n- `tester.longPress(finder)` — pressione prolungata\n\nTestiamo l'incremento del contatore.","testWidgets('Il tap incrementa il contatore', (tester) async {\n  await tester.pumpWidget(\n    const MaterialApp(home: Scaffold(body: Counter())),\n  );\n\n  expect(find.text('0'), findsOneWidget);\n\n  \u002F\u002F Simula il tap sul pulsante\n  await tester.tap(find.byType(ElevatedButton));\n  \u002F\u002F Ricostruisce l'albero per riflettere lo stato aggiornato\n  await tester.pump();\n\n  expect(find.text('0'), findsNothing);\n  expect(find.text('1'), findsOneWidget);\n\n  \u002F\u002F Un secondo tap porta a 2\n  await tester.tap(find.byType(ElevatedButton));\n  await tester.pump();\n  expect(find.text('2'), findsOneWidget);\n});","Dopo ogni tap il valore mostrato aumenta correttamente: 0 → 1 → 2.",{"id":60,"position":61,"title":62,"body":63,"code_snippet":64,"code_language":30,"expected_result":65,"demo_url":10,"video_url":10},242,6,"Gestire animazioni e stati asincroni con pumpAndSettle","Quando la UI contiene animazioni (es. transizioni, `SnackBar`, `showDialog`) un singolo `pump()` avanza di un solo frame e potrebbe non bastare. In questi casi usa `await tester.pumpAndSettle()`, che continua a generare frame finché non ci sono più animazioni in corso.\n\nSupponiamo che il tap mostri una `SnackBar`. Ecco come testarla. Nota che `pumpAndSettle` aspetta il completamento dell'animazione di comparsa della SnackBar.\n\n> **Suggerimento**: non usare `pumpAndSettle` con animazioni infinite (es. spinner di caricamento), altrimenti il test resterà bloccato in timeout. In quei casi usa `pump(Duration(...))`.","testWidgets('Mostra una SnackBar al tap', (tester) async {\n  await tester.pumpWidget(\n    MaterialApp(\n      home: Scaffold(\n        body: Builder(\n          builder: (context) => ElevatedButton(\n            onPressed: () {\n              ScaffoldMessenger.of(context).showSnackBar(\n                const SnackBar(content: Text('Fatto!')),\n              );\n            },\n            child: const Text('Mostra'),\n          ),\n        ),\n      ),\n    ),\n  );\n\n  await tester.tap(find.text('Mostra'));\n  await tester.pumpAndSettle(); \u002F\u002F attende l'animazione della SnackBar\n\n  expect(find.text('Fatto!'), findsOneWidget);\n});","Dopo il tap la SnackBar appare e il test trova il testo 'Fatto!'.",{"id":67,"position":68,"title":69,"body":70,"code_snippet":71,"code_language":30,"expected_result":72,"demo_url":10,"video_url":10},243,7,"Organizzare i test con group e setUp ed eseguirli","Per suite più grandi conviene raggruppare i test correlati con `group` e condividere la preparazione con `setUp`. Questo rende i test più leggibili e riduce la duplicazione.\n\nPer eseguire tutti i test:\n\n```bash\nflutter test\n```\n\nPer un singolo file:\n\n```bash\nflutter test test\u002Fcounter_test.dart\n```\n\nPer generare un report di copertura:\n\n```bash\nflutter test --coverage\n```\n\nIl file `coverage\u002Flcov.info` può essere visualizzato con strumenti come `genhtml` o estensioni dell'IDE. Ecco la struttura consigliata per i test del contatore.","void main() {\n  group('Counter', () {\n    \u002F\u002F Helper per montare il widget in ogni test\n    Future\u003Cvoid> montaCounter(WidgetTester tester) {\n      return tester.pumpWidget(\n        const MaterialApp(home: Scaffold(body: Counter())),\n      );\n    }\n\n    testWidgets('parte da 0', (tester) async {\n      await montaCounter(tester);\n      expect(find.text('0'), findsOneWidget);\n    });\n\n    testWidgets('incrementa al tap', (tester) async {\n      await montaCounter(tester);\n      await tester.tap(find.byType(ElevatedButton));\n      await tester.pump();\n      expect(find.text('1'), findsOneWidget);\n    });\n  });\n}","La suite è organizzata in gruppi leggibili e tutti i test passano eseguendo `flutter test`.",[],1785219608831]