hnvn / flutter_image_cropper

A Flutter plugin for Android and iOS supports cropping images
995 stars 392 forks source link

Provide test examples #305

Open ValentinVignal opened 2 years ago

ValentinVignal commented 2 years ago

Hello, I'm trying to write a test using your package, but I don't see any in this repository.

Could you add some so we can copy them?

daniloapr commented 2 years ago

I implemented the lib today and also couldn't see a way to properly test it, as we can't mock a static method. This is how I tested:

Create a "middleware" class. I called it ImageCropperService:

import 'dart:io';
import 'package:image_cropper/image_cropper.dart';

/// This service allows us to test the ImageCropper library, as the `cropImage`
/// is a static method and couldn't be mocked.
class ImageCropperService {
  // You can add other arguments from the original `cropImage` here as needed.
  Future<File?> cropImage({required String sourcePath}) {
    return ImageCropper.cropImage(
      sourcePath: sourcePath,
    );
  }
}

Inject the ImageCropperService in your code. In my case, I used get_it for it:

Future<void> cropImage() async {
  File? croppedImage = await GetIt.I<ImageCropperService>().cropImage(sourcePath: pickedImage.path);
  if (croppedImage == null) return;
  doSomethingWithCroppedImage(croppedImage);
}

Remember to register the ImageCropperService before the application starts (in the main.dart for example):

GetIt.I.registerFactory<ImageCropperService>(() => ImageCropperService());

Now in the test I used mocktail but you can also use mockito to mock the ImageCropperService.

import 'package:mocktail/mocktail.dart' as mocktail;

// Create the mock class
class ImageCropperMock extends mocktail.Mock implements ImageCropperService {}

void main(){
  ImageCropperService imageCropper;

  setupAll((){
    // Register the mock
    imageCropper = ImageCropperMock();
    GetIt.I.registerFactory<ImageCropperService>(() => imageCropper);
  });

  testWidget('description',  (WidgetTester tester) async {
    await pumpWidget(buildYourWidget());
    // Do your test actions, then verify if ImageCropperService was properly called:
    mocktail
      .verify(() => imageCropper.cropImage(sourcePath: 'your_expected_path'))
      .called(1);
  });
}
daniloapr commented 2 years ago

I believe it should be better if the cropImage wasn't static though, so we could mock the ImageCropper itself. If there is a better way, I will be glad to know it!

hnvn commented 2 years ago

Thanks @daniloapr, I agree with you, we should get rid of using static function. I am going to work on it

hnvn commented 2 years ago

I change cropImage to instance method and release v1.5.0. @daniloapr Could you help me provide test codes for the plugin?

hnvn commented 2 years ago

@daniloapr , I just migrate the plugin to federated plugins and publish it as v2.0.0-beta. If you want to provide unit test for the plugin, please consider to create PR on branch v2.

ValentinVignal commented 2 years ago

@hnvn I see your commit https://github.com/hnvn/flutter_image_cropper/commit/cd9843a8cc89fd003335036cb3e9b8f9f6ae10f5

May I suggest to do something like that instead?

class ImageCropper {
  ImageCropper._();

  static ImageCropper _instance = ImageCropper._();

  static ImageCropper get instance => _instance;

  @visibleForTesting
  static ImageCropper set instance(ImageCropper instance) {
    // So we can give a mock in the tests
    _instance = instance;
  }

  // Not a static method anymore.
  Future<File?> cropImage(/* ... */) {/* ... */}  
}

And instead of doing

ImageCropper.cropImage();

or

ImageCropper().cropImage();

it will be

ImageCropper.instance.cropImage();
hnvn commented 2 years ago

I have same approach in v2 branch, pls have a look at it: https://github.com/hnvn/flutter_image_cropper/blob/a6708c4d7e75d9b34763bd1aeb3fb9fe989235cd/image_cropper_platform_interface/lib/src/platform_interface/image_cropper_platform.dart#L34