如何在 Flutter 中模拟 VideoPlayerController.network()

Rah*_*ole 7 unit-testing flutter-test flutter-video-player flutter-mockito

我正在为使用 video_player 插件的视频播放器小部件编写一个简单的测试。我无法模拟视频控制器发出的用于通过网络获取视频的网络请求。我的小部件代码如下所示:

late VideoPlayerController _videoController;

@override
void initState() {
    _videoController = VideoPlayerController.network(widget.videoUrl);
// rest of the code
}
Run Code Online (Sandbox Code Playgroud)

和测试代码:

VideoPlayerController _videoController =
        VideoPlayerController.network(videoUrl);

    when(VideoPlayerController.network(videoUrl))
        .thenAnswer((_) => _videoController);
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为它无法正确地存根网络请求方法。有什么正确嘲笑它的想法吗?我的代码中还有其他几个测试,其中我模拟了发出网络请求的 api 客户端类,但这一个有点不同。我正在使用mockito进行嘲笑。

请帮忙!

Eng*_*Eng 1

解决方案:这就是模拟 video_player 包的方法。

// On unit test, you can use following
VideoPlayerPlatform.instance = FakeVideoPlayerPlatform();
Run Code Online (Sandbox Code Playgroud)
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

// For unit test
// ignore: depend_on_referenced_packages
import 'package:video_player_platform_interface/video_player_platform_interface.dart';

// Created from video_player package and video_player_test.dart file.
class FakeVideoPlayerPlatform extends VideoPlayerPlatform {
  final Completer<bool> initialized = Completer<bool>();
  final List<String> calls = <String>[];
  final List<DataSource> dataSources = <DataSource>[];
  final Map<int, StreamController<VideoEvent>> streams =
      <int, StreamController<VideoEvent>>{};
  final bool forceInitError;
  int nextTextureId = 0;
  final Map<int, Duration> _positions = <int, Duration>{};

  FakeVideoPlayerPlatform({
    this.forceInitError = false,
  });

  @override
  Future<int?> create(DataSource dataSource) async {
    calls.add('create');
    final StreamController<VideoEvent> stream = StreamController<VideoEvent>();
    streams[nextTextureId] = stream;
    if (forceInitError) {
      stream.addError(
        PlatformException(
          code: 'VideoError',
          message: 'Video player had error XYZ',
        ),
      );
    } else {
      stream.add(
        VideoEvent(
          eventType: VideoEventType.initialized,
          size: const Size(100, 100),
          duration: const Duration(seconds: 1),
        ),
      );
    }
    dataSources.add(dataSource);
    return nextTextureId++;
  }

  @override
  Future<void> dispose(int textureId) async {
    calls.add('dispose');
  }

  @override
  Future<void> init() async {
    calls.add('init');
    initialized.complete(true);
  }

  @override
  Stream<VideoEvent> videoEventsFor(int textureId) {
    return streams[textureId]!.stream;
  }

  @override
  Future<void> pause(int textureId) async {
    calls.add('pause');
  }

  @override
  Future<void> play(int textureId) async {
    calls.add('play');
  }

  @override
  Future<Duration> getPosition(int textureId) async {
    calls.add('position');
    return _positions[textureId] ?? Duration.zero;
  }

  @override
  Future<void> seekTo(int textureId, Duration position) async {
    calls.add('seekTo');
    _positions[textureId] = position;
  }

  @override
  Future<void> setLooping(int textureId, bool looping) async {
    calls.add('setLooping');
  }

  @override
  Future<void> setVolume(int textureId, double volume) async {
    calls.add('setVolume');
  }

  @override
  Future<void> setPlaybackSpeed(int textureId, double speed) async {
    calls.add('setPlaybackSpeed');
  }

  @override
  Future<void> setMixWithOthers(bool mixWithOthers) async {
    calls.add('setMixWithOthers');
  }

  @override
  Widget buildView(int textureId) {
    return Texture(textureId: textureId);
  }
}

Run Code Online (Sandbox Code Playgroud)