dart-lang / mockito

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

Can't generate mocks from classes using Patterns in return type. #651

Closed PatrickWulfe closed 1 year ago

PatrickWulfe commented 1 year ago

I decided to try and move away from using Dartz's Either class to use Dart 3's new patterns feature, but when I try to generate mocks, the generated code doesn't import any libraries for classes that appear in patterns, or if the library is imported, it won't prepend it to anything in the pattern.

Here is my test file:

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:mockito/annotations.dart';
import 'package:modular_test/modular_test.dart';
import 'package:sideboard/modules/card_search/card_search_index.dart';
import 'package:sideboard/modules/mtg_submodule/data/data_index.dart';

import 'card_search_bloc_test.mocks.dart';

@GenerateNiceMocks([
  MockSpec<Storage>(),
  MockSpec<ScryfallRepository>(),
  MockSpec<CardSearchBloc>(),
])
void main() {
  late ScryfallRepository scryfallRepository;
  late Storage storage;
  final cardSearchBloc = MockCardSearchBloc();

  setUp(() async {
    scryfallRepository = MockScryfallRepository();
    storage = MockStorage();
    HydratedBloc.storage = storage;

    initModule(
      CardSearchModule(),
      replaceBinds: [
        Bind.instance<ScryfallRepository>(scryfallRepository),
      ],
    );
  });

  group('card search bloc ...', () {
    whenListen(
      cardSearchBloc,
      Stream.fromIterable([const CardSearchState.initial()]),
    );
    blocTest<CardSearchBloc, CardSearchState>(
      'emits [Loading, Loaded] when SearchEvent is added.',
      build: () => cardSearchBloc,
      act: (bloc) => bloc.add(const CardSearchEvent.search('')),
      expect: () => [
        isA<CardSearchState>(),
      ],
    );
  });
}

Here is the generated file:

// Mocks generated by Mockito 5.4.1 from annotations
// in sideboard/test/modules/card_search/bloc/card_search_bloc_test.dart.
// Do not manually edit this file.

// @dart=2.19

// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i6;

import 'package:bloc/bloc.dart' as _i9;
import 'package:hydrated_bloc/src/hydrated_storage.dart' as _i5;
import 'package:mockito/mockito.dart' as _i1;
import 'package:mockito/src/dummies.dart' as _i8;
import 'package:scryfall_api/scryfall_api.dart' as _i2;
import 'package:sideboard/modules/card_search/bloc/card_search_bloc.dart'
    as _i4;
import 'package:sideboard/modules/mtg_submodule/data/data_index.dart' as _i7;
import 'package:sideboard/modules/mtg_submodule/domain/repository/mtg_repository.dart'
    as _i3;

// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class

class _FakeScryfallApiClient_0 extends _i1.SmartFake
    implements _i2.ScryfallApiClient {
  _FakeScryfallApiClient_0(
    Object parent,
    Invocation parentInvocation,
  ) : super(
          parent,
          parentInvocation,
        );
}

class _FakeMtgRepository_1 extends _i1.SmartFake implements _i3.MtgRepository {
  _FakeMtgRepository_1(
    Object parent,
    Invocation parentInvocation,
  ) : super(
          parent,
          parentInvocation,
        );
}

class _FakeCardSearchState_2 extends _i1.SmartFake
    implements _i4.CardSearchState {
  _FakeCardSearchState_2(
    Object parent,
    Invocation parentInvocation,
  ) : super(
          parent,
          parentInvocation,
        );
}

/// A class which mocks [Storage].
///
/// See the documentation for Mockito's code generation for more information.
class MockStorage extends _i1.Mock implements _i5.Storage {
  @override
  dynamic read(String? key) => super.noSuchMethod(
        Invocation.method(
          #read,
          [key],
        ),
        returnValueForMissingStub: null,
      );
  @override
  _i6.Future<void> write(
    String? key,
    dynamic value,
  ) =>
      (super.noSuchMethod(
        Invocation.method(
          #write,
          [
            key,
            value,
          ],
        ),
        returnValue: _i6.Future<void>.value(),
        returnValueForMissingStub: _i6.Future<void>.value(),
      ) as _i6.Future<void>);
  @override
  _i6.Future<void> delete(String? key) => (super.noSuchMethod(
        Invocation.method(
          #delete,
          [key],
        ),
        returnValue: _i6.Future<void>.value(),
        returnValueForMissingStub: _i6.Future<void>.value(),
      ) as _i6.Future<void>);
  @override
  _i6.Future<void> clear() => (super.noSuchMethod(
        Invocation.method(
          #clear,
          [],
        ),
        returnValue: _i6.Future<void>.value(),
        returnValueForMissingStub: _i6.Future<void>.value(),
      ) as _i6.Future<void>);
  @override
  _i6.Future<void> close() => (super.noSuchMethod(
        Invocation.method(
          #close,
          [],
        ),
        returnValue: _i6.Future<void>.value(),
        returnValueForMissingStub: _i6.Future<void>.value(),
      ) as _i6.Future<void>);
}

/// A class which mocks [ScryfallRepository].
///
/// See the documentation for Mockito's code generation for more information.
class MockScryfallRepository extends _i1.Mock
    implements _i7.ScryfallRepository {
  @override
  _i2.ScryfallApiClient get apiClient => (super.noSuchMethod(
        Invocation.getter(#apiClient),
        returnValue: _FakeScryfallApiClient_0(
          this,
          Invocation.getter(#apiClient),
        ),
        returnValueForMissingStub: _FakeScryfallApiClient_0(
          this,
          Invocation.getter(#apiClient),
        ),
      ) as _i2.ScryfallApiClient);
  @override
  _i6.Future<(Failure, MtgCardModel)> getMtgCardByID(String? id) =>
      (super.noSuchMethod(
        Invocation.method(
          #getMtgCardByID,
          [id],
        ),
        returnValue: _i6.Future<(Failure, MtgCardModel)>.value(
            _i8.dummyValue<(Failure, MtgCardModel)>(
          this,
          Invocation.method(
            #getMtgCardByID,
            [id],
          ),
        )),
        returnValueForMissingStub: _i6.Future<(Failure, MtgCardModel)>.value(
            _i8.dummyValue<(Failure, MtgCardModel)>(
          this,
          Invocation.method(
            #getMtgCardByID,
            [id],
          ),
        )),
      ) as _i6.Future<(Failure, MtgCardModel)>);
  @override
  _i6.Future<(Failure, PaginableList<MtgCardModel>)> getMtgCardsBySearch(
          String? searchStr) =>
      (super.noSuchMethod(
        Invocation.method(
          #getMtgCardsBySearch,
          [searchStr],
        ),
        returnValue: _i6.Future<(Failure, PaginableList<MtgCardModel>)>.value(
            _i8.dummyValue<(Failure, PaginableList<MtgCardModel>)>(
          this,
          Invocation.method(
            #getMtgCardsBySearch,
            [searchStr],
          ),
        )),
        returnValueForMissingStub:
            _i6.Future<(Failure, PaginableList<MtgCardModel>)>.value(
                _i8.dummyValue<(Failure, PaginableList<MtgCardModel>)>(
          this,
          Invocation.method(
            #getMtgCardsBySearch,
            [searchStr],
          ),
        )),
      ) as _i6.Future<(Failure, PaginableList<MtgCardModel>)>);
}

/// A class which mocks [CardSearchBloc].
///
/// See the documentation for Mockito's code generation for more information.
class MockCardSearchBloc extends _i1.Mock implements _i4.CardSearchBloc {
  @override
  _i3.MtgRepository get mtgRepository => (super.noSuchMethod(
        Invocation.getter(#mtgRepository),
        returnValue: _FakeMtgRepository_1(
          this,
          Invocation.getter(#mtgRepository),
        ),
        returnValueForMissingStub: _FakeMtgRepository_1(
          this,
          Invocation.getter(#mtgRepository),
        ),
      ) as _i3.MtgRepository);
  @override
  _i4.CardSearchState get state => (super.noSuchMethod(
        Invocation.getter(#state),
        returnValue: _FakeCardSearchState_2(
          this,
          Invocation.getter(#state),
        ),
        returnValueForMissingStub: _FakeCardSearchState_2(
          this,
          Invocation.getter(#state),
        ),
      ) as _i4.CardSearchState);
  @override
  _i6.Stream<_i4.CardSearchState> get stream => (super.noSuchMethod(
        Invocation.getter(#stream),
        returnValue: _i6.Stream<_i4.CardSearchState>.empty(),
        returnValueForMissingStub: _i6.Stream<_i4.CardSearchState>.empty(),
      ) as _i6.Stream<_i4.CardSearchState>);
  @override
  bool get isClosed => (super.noSuchMethod(
        Invocation.getter(#isClosed),
        returnValue: false,
        returnValueForMissingStub: false,
      ) as bool);
  @override
  void add(_i4.CardSearchEvent? event) => super.noSuchMethod(
        Invocation.method(
          #add,
          [event],
        ),
        returnValueForMissingStub: null,
      );
  @override
  void onEvent(_i4.CardSearchEvent? event) => super.noSuchMethod(
        Invocation.method(
          #onEvent,
          [event],
        ),
        returnValueForMissingStub: null,
      );
  @override
  void emit(_i4.CardSearchState? state) => super.noSuchMethod(
        Invocation.method(
          #emit,
          [state],
        ),
        returnValueForMissingStub: null,
      );
  @override
  void on<E extends _i4.CardSearchEvent>(
    _i9.EventHandler<E, _i4.CardSearchState>? handler, {
    _i9.EventTransformer<E>? transformer,
  }) =>
      super.noSuchMethod(
        Invocation.method(
          #on,
          [handler],
          {#transformer: transformer},
        ),
        returnValueForMissingStub: null,
      );
  @override
  void onTransition(
          _i9.Transition<_i4.CardSearchEvent, _i4.CardSearchState>?
              transition) =>
      super.noSuchMethod(
        Invocation.method(
          #onTransition,
          [transition],
        ),
        returnValueForMissingStub: null,
      );
  @override
  _i6.Future<void> close() => (super.noSuchMethod(
        Invocation.method(
          #close,
          [],
        ),
        returnValue: _i6.Future<void>.value(),
        returnValueForMissingStub: _i6.Future<void>.value(),
      ) as _i6.Future<void>);
  @override
  void onChange(_i9.Change<_i4.CardSearchState>? change) => super.noSuchMethod(
        Invocation.method(
          #onChange,
          [change],
        ),
        returnValueForMissingStub: null,
      );
  @override
  void addError(
    Object? error, [
    StackTrace? stackTrace,
  ]) =>
      super.noSuchMethod(
        Invocation.method(
          #addError,
          [
            error,
            stackTrace,
          ],
        ),
        returnValueForMissingStub: null,
      );
  @override
  void onError(
    Object? error,
    StackTrace? stackTrace,
  ) =>
      super.noSuchMethod(
        Invocation.method(
          #onError,
          [
            error,
            stackTrace,
          ],
        ),
        returnValueForMissingStub: null,
      );
}
PatrickWulfe commented 1 year ago

After cloning the repo and adding the following to the analysis_options.yaml fixed it for me, though I doubt this is an optimal solution.

analyzer:
  enable-experiment:
    - records
srawlins commented 1 year ago

Thanks for the report. I think you mean that there is an issue with record types in a return value. I can't be sure without seeing the declaration of ScryfallRepository. If this is the case, this is a duplicate of https://github.com/dart-lang/mockito/issues/636

PatrickWulfe commented 1 year ago

It is in fact a duplicate issue, didn't catch it when I searched. Thanks!

yanok commented 1 year ago

This is fixed in master branch but not released yet.