Flutter: readAsBytes/readAsString в тестах виджетов

Я пытаюсь проверить StatefulWidget со следующими onPressed функция.

      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 кажется readAsBytes работает в отдельном изоляторе и, похоже, не завершает readAsBytes()будущее в тестовом изоляте. Я бы не хотел использовать синхронизирующую версию этого метода. Вы знаете, как этого добиться?

1 ответ

Я также столкнулся с этой проблемой и не смог понять, почему чтение файла никогда не выполнялось (или не возникало никаких исключений).

В итоге я использовал пакет файлов, чтобы заменить 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


Другие вопросы по тегам