简体   繁体   English

Flutter:小部件测试中的 readAsBytes/readAsString

[英]Flutter: readAsBytes/readAsString in widget tests

I am trying to test a StatefulWidget with the following onPressed function.我正在尝试使用以下onPressed函数测试StatefulWidget

      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.camera_alt),
        onPressed: () async {
          try {
            final filename = widget.randomStringGenerator?.call() ?? '';
            final path = join(
              (await getTemporaryDirectory()).path,
              '${filename}.png',
            );
            await widget.cameraController?.takePicture(path);
            final file = File(path);
            final image = await file.readAsBytes();
            widget.onPhotoTaken(image);
            await file.delete();
          } catch (e) {
            print(e);
          }
        },
      ),

and the following widget test:以及以下小部件测试:

void main() {
  Directory directory;
  group('PhotoWidget test', () {
    setUp(() async {
      // Create a temporary directory.
      directory = await Directory.systemTemp.createTemp();
      // Mock out the MethodChannel for the path_provider plugin.
      const MethodChannel('plugins.flutter.io/path_provider')
          .setMockMethodCallHandler(
        (MethodCall methodCall) async {
          if (methodCall.method == 'getTemporaryDirectory') {
            return directory.path;
          }
          return null;
        },
      );
    });

    tearDown(() async {
      await directory?.delete(
        recursive: true,
      );
    });

    testWidgets('onPictureTaken is called', (WidgetTester tester) async {
      // GIVEN
      Uint8List callbackImage;
      var expectedImage = base64.decode(
        'iVBORw0KGgoAAAANSUh'
        'EUgAAAAEAAAABCAYAAAA'
        'fFcSJAAAADUlEQVR42mNk+'
        'P+/HgAFhAJ/wlseKgAAAA'
        'BJRU5ErkJggg==',
      );
      var randomStringGenerator = () => 'test_simple_photo';
      // Join file path
      final file = File(join(
        directory.path,
        '${randomStringGenerator()}.png',
      ));
      // write image to expected path
      file.writeAsBytesSync(expectedImage);
      var cameraController = MockCameraController();
      when(cameraController.initialize()).thenAnswer((_) => Future.value());
      when(cameraController.value).thenReturn(
        CameraValue(
          isInitialized: false,
        ),
      );
      var photoPage = SimplePhotoPage(
        cameraController: cameraController,
        randomStringGenerator: randomStringGenerator,
        onPhotoTaken: (image) => callbackImage = image,
      );
      // WHEN
      await tester.pumpWidget(
        MaterialApp(
          home: photoPage,
        ),
      );
      await tester.tap(find.byType(FloatingActionButton));
      await tester.pumpAndSettle();
      // THEN
      expect(callbackImage, expectedImage);
    });
  });
}

However, this test fails and when debugging the test exits at但是,此测试失败,调试时测试退出

final image = await file.readAsBytes();

without any error or really any sign that anything went wrong.没有任何错误,也没有任何迹象表明出现任何问题。 The interesting thing is when I switch to their sync counterparts ( readAsBytesSync() , deleteSync() ), the test passes.有趣的是,当我切换到它们的同步对应项( readAsBytesSync()deleteSync() )时,测试通过了。

From reading the source code for the dart io lib it seems readAsBytes runs in a separate isolate and it doesn't seem to complete the readAsBytes() future in the test isolate.从阅读 dart io lib 的源代码readAsBytes似乎在单独的隔离中运行,并且它似乎没有在测试隔离中完成readAsBytes()未来。 I would like not to use the sync version of this method.我不想使用此方法的同步版本。 Do you guys know how to accomplish this?大家知道怎么实现吗?

I also faced this issue, and wasn't able to figure out why the file read never executed (or no exceptions were thrown).我也遇到了这个问题,无法弄清楚为什么读取的文件从未执行过(或没有抛出异常)。

I ended up is using file package to replace 'dart:io' to pass in an interface to the widget for accessing the filesystem.我最终使用文件包来替换 'dart:io' 以将接口传递给小部件以访问文件系统。 This creates a constructor as such这将创建一个构造函数

import 'package:file/memory.dart';
import 'package:file/file.dart'

class FSWidget extends StatefulWidget{
  FileSystem _fs;
  
  FSWidget({FilesSystem? fileSystem}): _fs = fileSystem?? MemoryFileSystem();

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

}

This allows you to use to pass in your own filesystem for testing这允许您使用传入您自己的文件系统进行测试

Your temp file system access code becomes您的临时文件系统访问代码变为

...
final filename = widget.randomStringGenerator?.call() ?? '';
final tempDirectory = await widget._fs.systemTempDirectory.createTemp('tempDir');
final outputFile = tempDirectory.childFile('${filename}.png');

await widget.cameraController?.takePicture(outputFile.path);
widget.onPhotoTaken(outputFile);
await outputFile.delete();

...

You can now pass in a reference to your test filesystem to your widget您现在可以将测试文件系统的引用传递给您的小部件

testWidgets('onPictureTaken is called', (WidgetTester tester) async {
  FileSystem testFS = MemoryFileSystem();
  // Add stuff to the filesystem
  ...
  await tester.pumpWidget(
    MaterialApp(
      home: FSWidget(filesystem: testFS),
    ),
  ...
  // Expect stuff from the filesystem


声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM