dart-lang / mockito

Mockito-inspired mock library for Dart
https://pub.dev/packages/mockito
Apache License 2.0
636 stars 163 forks source link

Mocking Getx controller for flutter widget testing #446

Closed ghost closed 2 years ago

ghost commented 3 years ago

I am getting two exception in the GeneratedMock file:

  1. This exception can be found in the _FakeRx, _FakeRxList and _FakeRxBool 'Object.==' ('bool Function(Object)') isn't a valid concrete implementation of 'RxObjectMixin.==' ('bool Function(dynamic)').

  2. This exception is given for _i3._InternalFinalCallback Classes and mixins can only implement other classes. Try specifying a class or mixin, or remove the name from the list.

How would you solve this issue and why am I getting it? (Sorry if it is something obvious, first time using mockito and getx )

Full code:

// Mocks generated by Mockito 5.0.10 from annotations
// in bloodglucose_monitoring_flutterapp/test/widget/widget_test.dart.
// Do not manually edit this file.

import 'dart:async' as _i6;
import 'dart:ui' as _i7;

import 'package:bloodglucose_monitoring_flutterapp/controller/glucose_controller.dart'
    as _i4;
import 'package:bloodglucose_monitoring_flutterapp/model/glucose.dart' as _i5;
import 'package:get/get_instance/src/lifecycle.dart' as _i3;
import 'package:get/get_rx/src/rx_types/rx_types.dart' as _i2;
import 'package:get/get_state_manager/src/simple/list_notifier.dart' as _i8;
import 'package:mockito/mockito.dart' as _i1;

// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: comment_references
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis

class _FakeRx<T> extends _i1.Fake implements _i2.Rx<T> {}

class _FakeRxList<E> extends _i1.Fake implements _i2.RxList<E> {}

class _FakeRxBool extends _i1.Fake implements _i2.RxBool {
  @override
  String toString() => super.toString();
}

class _Fake_InternalFinalCallback<T> extends _i1.Fake
    implements _i3._InternalFinalCallback<T> {}

/// A class which mocks [GlucoseController].
///
/// See the documentation for Mockito's code generation for more information.
class MockGlucoseController extends _i1.Mock implements _i4.GlucoseController {
  MockGlucoseController() {
    _i1.throwOnMissingStub(this);
  }

  @override
  _i2.Rx<_i4.GlucoseListStatus> get getStatus =>
      (super.noSuchMethod(Invocation.getter(#getStatus),
              returnValue: _FakeRx<_i4.GlucoseListStatus>())
          as _i2.Rx<_i4.GlucoseListStatus>);
  @override
  _i2.RxList<DateTime> get getFilteredStartDate =>
      (super.noSuchMethod(Invocation.getter(#getFilteredStartDate),
          returnValue: _FakeRxList<DateTime>()) as _i2.RxList<DateTime>);
  @override
  _i2.RxList<DateTime> get getFilteredEndDate =>
      (super.noSuchMethod(Invocation.getter(#getFilteredEndDate),
          returnValue: _FakeRxList<DateTime>()) as _i2.RxList<DateTime>);
  @override
  _i2.RxList<_i5.Glucose> get getDateFilteredGlucoseList =>
      (super.noSuchMethod(Invocation.getter(#getDateFilteredGlucoseList),
          returnValue: _FakeRxList<_i5.Glucose>()) as _i2.RxList<_i5.Glucose>);
  @override
  _i2.RxList<_i5.Glucose> get getMinimumGlucoseValue =>
      (super.noSuchMethod(Invocation.getter(#getMinimumGlucoseValue),
          returnValue: _FakeRxList<_i5.Glucose>()) as _i2.RxList<_i5.Glucose>);
  @override
  _i2.RxList<_i5.Glucose> get getMaximumGlucoseValue =>
      (super.noSuchMethod(Invocation.getter(#getMaximumGlucoseValue),
          returnValue: _FakeRxList<_i5.Glucose>()) as _i2.RxList<_i5.Glucose>);
  @override
  _i2.RxList<double> get getAverageGlucoseValue =>
      (super.noSuchMethod(Invocation.getter(#getAverageGlucoseValue),
          returnValue: _FakeRxList<double>()) as _i2.RxList<double>);
  @override
  _i2.RxList<double> get getMedianGlucoseValue =>
      (super.noSuchMethod(Invocation.getter(#getMedianGlucoseValue),
          returnValue: _FakeRxList<double>()) as _i2.RxList<double>);
  @override
  _i2.RxBool get getIsMaximumGlucoseValueHover =>
      (super.noSuchMethod(Invocation.getter(#getIsMaximumGlucoseValueHover),
          returnValue: _FakeRxBool()) as _i2.RxBool);
  @override
  _i2.RxBool get getIsMinimumGlucoseValueHover =>
      (super.noSuchMethod(Invocation.getter(#getIsMinimumGlucoseValueHover),
          returnValue: _FakeRxBool()) as _i2.RxBool);
  @override
  bool get hasListeners =>
      (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
          as bool);
  @override
  int get listeners =>
      (super.noSuchMethod(Invocation.getter(#listeners), returnValue: 0)
          as int);
  @override
  bool get initialized =>
      (super.noSuchMethod(Invocation.getter(#initialized), returnValue: false)
          as bool);
  @override
  bool get isClosed =>
      (super.noSuchMethod(Invocation.getter(#isClosed), returnValue: false)
          as bool);
  @override
  _i3._InternalFinalCallback<void> get onStart =>
      (super.noSuchMethod(Invocation.getter(#onStart),
              returnValue: _Fake_InternalFinalCallback<void>())
          as _i3._InternalFinalCallback<void>);
  @override
  _i3._InternalFinalCallback<void> get onDelete =>
      (super.noSuchMethod(Invocation.getter(#onDelete),
              returnValue: _Fake_InternalFinalCallback<void>())
          as _i3._InternalFinalCallback<void>);
  @override
  _i6.Future<void> onInit() =>
      (super.noSuchMethod(Invocation.method(#onInit, []),
          returnValue: Future<void>.value(),
          returnValueForMissingStub: Future.value()) as _i6.Future<void>);
  @override
  void dispose() => super.noSuchMethod(Invocation.method(#dispose, []),
      returnValueForMissingStub: null);
  @override
  void setFilteredStartDate(DateTime? startDate) =>
      super.noSuchMethod(Invocation.method(#setFilteredStartDate, [startDate]),
          returnValueForMissingStub: null);
  @override
  void setFilteredEndDate(DateTime? endDate) =>
      super.noSuchMethod(Invocation.method(#setFilteredEndDate, [endDate]),
          returnValueForMissingStub: null);
  @override
  void setIsMaximumGlucoseValueHover() =>
      super.noSuchMethod(Invocation.method(#setIsMaximumGlucoseValueHover, []),
          returnValueForMissingStub: null);
  @override
  void setIsMinimumGlucoseValueHover() =>
      super.noSuchMethod(Invocation.method(#setIsMinimumGlucoseValueHover, []),
          returnValueForMissingStub: null);
  @override
  _i6.Future<void> postGlucoseData() =>
      (super.noSuchMethod(Invocation.method(#postGlucoseData, []),
          returnValue: Future<void>.value(),
          returnValueForMissingStub: Future.value()) as _i6.Future<void>);
  @override
  _i6.Future<void> saveGlucoseData() =>
      (super.noSuchMethod(Invocation.method(#saveGlucoseData, []),
          returnValue: Future<void>.value(),
          returnValueForMissingStub: Future.value()) as _i6.Future<void>);
  @override
  void update([List<Object>? ids, bool? condition = true]) =>
      super.noSuchMethod(Invocation.method(#update, [ids, condition]),
          returnValueForMissingStub: null);
  @override
  void refresh() => super.noSuchMethod(Invocation.method(#refresh, []),
      returnValueForMissingStub: null);
  @override
  void refreshGroup(Object? id) =>
      super.noSuchMethod(Invocation.method(#refreshGroup, [id]),
          returnValueForMissingStub: null);
  @override
  void notifyChildrens() =>
      super.noSuchMethod(Invocation.method(#notifyChildrens, []),
          returnValueForMissingStub: null);
  @override
  void removeListener(_i7.VoidCallback? listener) =>
      super.noSuchMethod(Invocation.method(#removeListener, [listener]),
          returnValueForMissingStub: null);
  @override
  void removeListenerId(Object? id, _i7.VoidCallback? listener) =>
      super.noSuchMethod(Invocation.method(#removeListenerId, [id, listener]),
          returnValueForMissingStub: null);
  @override
  _i8.Disposer addListener(_i8.GetStateUpdate? listener) =>
      (super.noSuchMethod(Invocation.method(#addListener, [listener]),
          returnValue: () {}) as _i8.Disposer);
  @override
  _i8.Disposer addListenerId(Object? key, _i8.GetStateUpdate? listener) =>
      (super.noSuchMethod(Invocation.method(#addListenerId, [key, listener]),
          returnValue: () {}) as _i8.Disposer);
  @override
  void disposeId(Object? id) =>
      super.noSuchMethod(Invocation.method(#disposeId, [id]),
          returnValueForMissingStub: null);
  @override
  void onReady() => super.noSuchMethod(Invocation.method(#onReady, []),
      returnValueForMissingStub: null);
  @override
  void onClose() => super.noSuchMethod(Invocation.method(#onClose, []),
      returnValueForMissingStub: null);
  @override
  void $configureLifeCycle() =>
      super.noSuchMethod(Invocation.method(#$configureLifeCycle, []),
          returnValueForMissingStub: null);
}
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.2.3, on macOS 11.4 20F71 darwin-x64, locale en-LK)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.1)
[✓] IntelliJ IDEA Community Edition (version 2021.1.1)
[✓] VS Code (version 1.58.0)
[✓] Connected device (1 available)
srawlins commented 3 years ago

This exception can be found in the _FakeRx, _FakeRxList and _FakeRxBool 'Object.==' ('bool Function(Object)') isn't a valid concrete implementation of 'RxObjectMixin.==' ('bool Function(dynamic)').

I suspect this problem is fixed in a recent version of get. At HEAD, you can see that RxObjectMixin.== accepts an Object instead of dynamic: https://github.com/jonataslaw/getx/blob/master/lib/get_rx/src/rx_types/rx_core/rx_impl.dart#L84

implements _i3._InternalFinalCallback {}

Ouch, mockito should definitely throw rather than generate this code. In any case, the problem is that GlucoseController has methods which seem to reference private types: get onStart and get onDelete return _InternalFinalCallback<void> which we can't mock...

You might be able to get around this shortcoming by specifying your own fallback generators (in customMocks for these two methods. These are a new API so not documented super well. You can find an example in the tests.

srawlins commented 3 years ago

Indeed it looks like the get authors do not want GetLifeCycle to be subclassed, as per onStart:

It uses an internal "callable" type, to avoid any @overrides in subclases. This method should be internal and is required to define the lifetime cycle of the subclass.

I understand that you are likely not overriding the getter, but mockito needs to, in order to return a non-null value.

kuhnroyal commented 2 years ago

The changes related to this issue broke quite a few of my mocks which previously worked. I understand that I probably have not used the broken generated methods and that is why they work. But this is likely a breaking change for a lot of projects and should not be patch version.

srawlins commented 2 years ago

@kuhnroyal You may have responded to the wrong issue. I don't see how your comments relate to this issue...

The changes related to this issue broke

What changes?

But this is likely a breaking change

What is?

kuhnroyal commented 2 years ago

You linked it in the changelog for 5.0.17 - in any case, that version breaks builds and it looks like this fits as I suddenly have invalid mocks.

srawlins commented 2 years ago

Ah I understand. Thanks for the info. Can you please file an issue with the problem with a reproduction case? Including the mocks-in-question generated by 5.0.16 and the error reported by 5.0.17 would be very helpful. Thanks!

fauzipadlaw commented 10 months ago

solution for this issue?