flutterdartdart-asyncdart-test

Write a test for reading and writing files in dart


I am learning Flutter and Dart currently. Now I want to read and write files to memory. I have code for reading and writing. Now I want tests for that. Here is where I run into problems. I always get:

'package:flutter/src/services/platform_channel.dart': Failed assertion: line 134 pos 7: '_binaryMessenger != null || ServicesBinding.instance != null': Cannot use this MethodChannel before the binary messenger has been initialized. This happens when you invoke platform methods before the WidgetsFlutterBinding has been initialized. You can fix this by either calling WidgetsFlutterBinding.ensureInitialized() before this or by passing a custom BinaryMessenger instance to MethodChannel().
dart:core                                                   _AssertionError._throwNew
package:flutter/src/services/platform_channel.dart 134:7    MethodChannel.binaryMessenger
package:flutter/src/services/platform_channel.dart 167:36   MethodChannel._invokeMethod
package:flutter/src/services/platform_channel.dart 350:12   MethodChannel.invokeMethod
package:path_provider_macos/path_provider_macos.dart 48:10  PathProviderMacOS.getApplicationDocumentsPath
package:path_provider/path_provider.dart 115:40             getApplicationDocumentsDirectory
package:skeet25pro/main_counter.dart 18:29                  CounterStorage._localPath
package:skeet25pro/main_counter.dart 24:24                  CounterStorage._localFile
package:skeet25pro/main_counter.dart 43:24                  CounterStorage.writeCounter
test/file_io_test.dart 8:27                                 main.<fn>
test/file_io_test.dart 5:33                                 main.<fn>

main_counter.dart

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Reading and Writing Files',
      home: FlutterDemo(storage: CounterStorage()),
    ),
  );
}

class CounterStorage {
  Future<String> get _localPath async {
    final directory = await getApplicationDocumentsDirectory();

    return directory.path;
  }

  Future<File> get _localFile async {
    final path = await _localPath;
    return File('$path/counter.txt');
  }

  Future<int> readCounter() async {
    try {
      final file = await _localFile;

      // Read the file
      final contents = await file.readAsString();

      return int.parse(contents);
    } catch (e) {
      // If encountering an error, return 0
      return 0;
    }
  }

  Future<File> writeCounter(int counter) async {
    final file = await _localFile;

    // Write the file
    return file.writeAsString('$counter');
  }
}

class FlutterDemo extends StatefulWidget {
  const FlutterDemo({Key? key, required this.storage}) : super(key: key);

  final CounterStorage storage;

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

class _FlutterDemoState extends State<FlutterDemo> {
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    widget.storage.readCounter().then((int value) {
      setState(() {
        _counter = value;
      });
    });
  }

  Future<File> _incrementCounter() {
    setState(() {
      _counter++;
    });

    // Write the variable as a string to the file.
    return widget.storage.writeCounter(_counter);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Reading and Writing Files'),
      ),
      body: Center(
        child: Text(
          'Button tapped $_counter time${_counter == 1 ? '' : 's'}.',
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

file_io_test.dart

import 'package:flutter_test/flutter_test.dart';
import 'package:skeet25pro/main_counter.dart';

void main() {
  test('Check file save works', () async {
    final CounterStorage storage = CounterStorage();
    var counter = 6;
    var t = await storage.writeCounter(counter);
    expect(1, 1);
  });
}

When I run the app through a simulator, it works perfectly fine. I would really like to get the tests running.

EDIT: If I try and add WidgetsFlutterBinding.ensureInitialized();

void main() {
  test('Check file save works', () async {
    WidgetsFlutterBinding.ensureInitialized();
    final CounterStorage storage = CounterStorage();
    var counter = 6;
    var t = await storage.writeCounter(counter);
    expect(1, 1);
  });
}

I get the error:

MissingPluginException(No implementation found for method getApplicationDocumentsDirectory on channel plugins.flutter.io/path_provider_macos)
package:flutter/src/services/platform_channel.dart 175:7  MethodChannel._invokeMethod

Seems like one should use something like: setMockMethodCallHandler to intercept the call to the different directory providers. Still no working solution.


Solution

  • You have to mock the path_provider call and maybe put the WidgetsFlutterBinding.ensureInitialized(); at the beginning of main. I guess you want something like

    Future<void> main() async {
      TestWidgetsFlutterBinding.ensureInitialized();
    
      setUpAll(() {
        const channel = MethodChannel(
          'plugins.flutter.io/path_provider_macos',
        );
        channel.setMockMethodCallHandler((MethodCall methodCall) async {
          switch (methodCall.method) {
            case 'getApplicationDocumentsDirectory':
              return "PATH_TO_MOCK_DIR";
            default:
          }
        });
      });
    
      test('Check file save works', () async {
        final CounterStorage storage = CounterStorage();
        var counter = 6;
        var t = await storage.writeCounter(counter);
        expect(1, 1);
      });
    }```