Search code examples
flutterflutter-test

How to test state in flutter?


So I have a simple counter app,

class CounterApp extends StatefulWidget {
  const CounterApp({Key? key}) : super(key: key);

  @override
  _CounterAppState createState() => _CounterAppState();
}

class _CounterAppState extends State<CounterApp> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Text(_counter.toString()),
      ),
    );
  }
}

So how do I test the _counter state?

I tried doing this,

testWidgets("counter", (tester) async {
  const key = Key("counter");
  await tester.pumpWidget(const CounterApp(key: key));

  final state = tester.state(find.byKey(key));

  expect(state._counter, 0);
});

But I get error Error: The getter '_counter' isn't defined for the class. Are we even supposed to test state?


Solution

  • First, you need to specify the type while using state method to avoid compiling errors:

    final _CounterAppState state = tester.state(find.byKey(key));
    

    Second, _CounterAppState and _counter are private and you shouldn't test private classes/variables directly. You can make the class public and provide a public getter for the private variable:

    int get testCounter => _counter;
    

    However, this is a way to access private declarations which I wouldn't recommend. Instead, I would recommend annotating your public variable/class with @visibleForTesting to make dart analyzer warn you if annotated code is not used in a private way. Don't forget to import foundation or meta library.

    visibleForTesting top-level constant

    Used to annotate a declaration that was made public, so that it is more visible than otherwise necessary, to make code testable.

    Tools, such as the analyzer, can provide feedback if

    • the annotation is associated with a declaration not in the lib folder of a package, or a private declaration, or a declaration in an unnamed static extension, or
    • the declaration is referenced outside of its defining library or a library which is in the test folder of the defining package.

    Here is the implementation:

    // Import the foundation library
    import 'package:flutter/foundation.dart';
    
    class CounterApp extends StatefulWidget {
      const CounterApp({Key? key}) : super(key: key);
    
      @override
      CounterAppState createState() => CounterAppState();
    }
    
    // Add the annotation above the class
    @visibleForTesting
    class CounterAppState extends State<CounterApp> {
      // Add the annotation above the variable
      @visibleForTesting
      int counter = 0;
    
      void _incrementCounter() {
        setState(() {
          counter++;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: Text(counter.toString()),
          ),
        );
      }
    }
    

    You might want to remove the annotation after testing.