3

我正在尝试StatefulWidget使用以下onPressed功能测试 a 。

      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);
          }
        },
      ),

和以下小部件测试:

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);
    });
  });
}

但是,此测试失败,并且在调试测试时退出

final image = await file.readAsBytes();

没有任何错误或任何迹象表明出现任何问题。有趣的是,当我切换到他们的同步对应项(readAsBytesSync(), deleteSync())时,测试通过了。

从阅读 dart io lib 的源代码来看,它似乎readAsBytes在单独的隔离中运行,并且似乎没有readAsBytes()在测试隔离中完成未来。我不想使用此方法的同步版本。你们知道如何做到这一点吗?

4

1 回答 1

0

我也遇到了这个问题,并且无法弄清楚为什么文件读取从未执行(或没有引发异常)。

我最终使用文件包替换“dart:io”以将接口传递给小部件以访问文件系统。这会创建一个构造函数

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();
  }

}

这允许您使用传递自己的文件系统进行测试

您的临时文件系统访问代码变为

...
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();

...

您现在可以将对测试文件系统的引用传递给您的小部件

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


于 2021-05-21T14:50:49.087 回答