Search code examples
unit-testingflutter-testflutter-video-playerflutter-mockito

How to mock VideoPlayerController.network() in Flutter


I'm writing a simple test for my video player widget that uses the video_player plugin. I'm not able to mock the network request made by the video controller for fetching the video over network. My widget code looks like:

late VideoPlayerController _videoController;

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

and test code:

VideoPlayerController _videoController =
        VideoPlayerController.network(videoUrl);

    when(VideoPlayerController.network(videoUrl))
        .thenAnswer((_) => _videoController);

This is not working because it is not able to stub the network request method correctly. Any ideas for mocking it correctly? I have several other tests in my code where I have mocked my api client class which makes network requests, but this one is a little different. I'm using mockito for mocking.

Please help!


Solution

  • Solution : This is how you can mock the video_player package.

    // On unit test, you can use following
    VideoPlayerPlatform.instance = FakeVideoPlayerPlatform();
    
    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);
      }
    }