Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Two tests with same code with different result (old version and new version of mockito)? #401

Closed
EhiltonKazuo opened this issue May 1, 2021 · 6 comments

Comments

@EhiltonKazuo
Copy link

EhiltonKazuo commented May 1, 2021

Good afternoon,
I trying to mock Hive dependency, but no succeed. I tested the older version of mockito and it granted success, but when i use the mockito with null safety occur an error. I mocked the HiveInterface and Box in all tests.

Here is the old mockito version:

import 'package:hive/hive.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';

class MockHiveInterface extends Mock implements HiveInterface {}

class MockHiveBox extends Mock implements Box {}

class PersonRepository {
  final HiveInterface hive;

  PersonRepository(this.hive);

  Future<void> save(Map person) async {
    var box = await hive.openBox('persons');
    await box.add(person);
  }
}

void main() {
  MockHiveInterface hive;
  MockHiveBox hiveBox;
  PersonRepository sut;
  Map person;

  setUp(() {
    hive = MockHiveInterface();
    hiveBox = MockHiveBox();
    sut = PersonRepository(hive);
    person = {'name': 'John Doe', 'age': 18};
  });

  test('Should call Hive', () async {
    //arrange
    when(hive.openBox(any)).thenAnswer((_) async => hiveBox);
    when(hiveBox.add(any)).thenAnswer((_) async => 1);
    //act
    await sut.save(person);
    //assert
    verify(hive.openBox('persons'));
    verify(hiveBox.add(person));
  });
}

Here is the new version:

import 'package:hive/hive.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';

import 'hive_test.mocks.dart';

class PersonRepository {
  final HiveInterface hive;

  PersonRepository(this.hive);

  Future<void> save(Map person) async {
    var box = await hive.openBox('persons');
    box.add(person);
  }
}

@GenerateMocks([HiveInterface, Box])
void main() {
  late MockHiveInterface hive;
  late MockBox hiveBox;
  late PersonRepository sut;
  late Map person;

  setUp(() {
    hive = MockHiveInterface();
    hiveBox = MockBox();
    sut = PersonRepository(hive);
    person = {'name': 'John Doe', 'age': 18};
  });

  test('Should call Hive', () async {
    //arrange
    when(hive.openBox(any)).thenAnswer((_) async => hiveBox);
    when(hiveBox.add(any)).thenAnswer((_) async => 1);
    //act
    await sut.save(person);
    //assert
    verify(hive.openBox('persons'));
    verify(hiveBox.add(person));
  });
}

The *.mocks.dart code:

// Mocks generated by Mockito 5.0.7 from annotations
// in hive_null_safety/test/hive_test.dart.
// Do not manually edit this file.

import 'dart:async' as _i3;
import 'dart:typed_data' as _i6;

import 'package:hive/hive.dart' as _i2;
import 'package:hive/src/box/default_compaction_strategy.dart' as _i5;
import 'package:hive/src/box/default_key_comparator.dart' as _i4;
import 'package:mockito/mockito.dart' as _i1;

// ignore_for_file: comment_references
// ignore_for_file: unnecessary_parenthesis

// ignore_for_file: prefer_const_constructors

// ignore_for_file: avoid_redundant_argument_values

class _FakeBox<E> extends _i1.Fake implements _i2.Box<E> {}

class _FakeLazyBox<E> extends _i1.Fake implements _i2.LazyBox<E> {}

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

  @override
  void init(String? path) =>
      super.noSuchMethod(Invocation.method(#init, [path]),
          returnValueForMissingStub: null);
  @override
  _i3.Future<_i2.Box<E>> openBox<E>(String? name,
          {_i2.HiveCipher? encryptionCipher,
          _i2.KeyComparator? keyComparator = _i4.defaultKeyComparator,
          _i2.CompactionStrategy? compactionStrategy =
              _i5.defaultCompactionStrategy,
          bool? crashRecovery = true,
          String? path,
          _i6.Uint8List? bytes,
          List<int>? encryptionKey}) =>
      (super.noSuchMethod(
              Invocation.method(#openBox, [
                name
              ], {
                #encryptionCipher: encryptionCipher,
                #keyComparator: keyComparator,
                #compactionStrategy: compactionStrategy,
                #crashRecovery: crashRecovery,
                #path: path,
                #bytes: bytes,
                #encryptionKey: encryptionKey
              }),
              returnValue: Future<_i2.Box<E>>.value(_FakeBox<E>()))
          as _i3.Future<_i2.Box<E>>);
  @override
  _i3.Future<_i2.LazyBox<E>> openLazyBox<E>(String? name,
          {_i2.HiveCipher? encryptionCipher,
          _i2.KeyComparator? keyComparator = _i4.defaultKeyComparator,
          _i2.CompactionStrategy? compactionStrategy =
              _i5.defaultCompactionStrategy,
          bool? crashRecovery = true,
          String? path,
          List<int>? encryptionKey}) =>
      (super.noSuchMethod(
              Invocation.method(#openLazyBox, [
                name
              ], {
                #encryptionCipher: encryptionCipher,
                #keyComparator: keyComparator,
                #compactionStrategy: compactionStrategy,
                #crashRecovery: crashRecovery,
                #path: path,
                #encryptionKey: encryptionKey
              }),
              returnValue: Future<_i2.LazyBox<E>>.value(_FakeLazyBox<E>()))
          as _i3.Future<_i2.LazyBox<E>>);
  @override
  _i2.Box<E> box<E>(String? name) =>
      (super.noSuchMethod(Invocation.method(#box, [name]),
          returnValue: _FakeBox<E>()) as _i2.Box<E>);
  @override
  _i2.LazyBox<E> lazyBox<E>(String? name) =>
      (super.noSuchMethod(Invocation.method(#lazyBox, [name]),
          returnValue: _FakeLazyBox<E>()) as _i2.LazyBox<E>);
  @override
  bool isBoxOpen(String? name) =>
      (super.noSuchMethod(Invocation.method(#isBoxOpen, [name]),
          returnValue: false) as bool);
  @override
  _i3.Future<void> close() => (super.noSuchMethod(Invocation.method(#close, []),
      returnValue: Future<void>.value(null),
      returnValueForMissingStub: Future.value()) as _i3.Future<void>);
  @override
  _i3.Future<void> deleteBoxFromDisk(String? name) =>
      (super.noSuchMethod(Invocation.method(#deleteBoxFromDisk, [name]),
          returnValue: Future<void>.value(null),
          returnValueForMissingStub: Future.value()) as _i3.Future<void>);
  @override
  _i3.Future<void> deleteFromDisk() =>
      (super.noSuchMethod(Invocation.method(#deleteFromDisk, []),
          returnValue: Future<void>.value(null),
          returnValueForMissingStub: Future.value()) as _i3.Future<void>);
  @override
  List<int> generateSecureKey() =>
      (super.noSuchMethod(Invocation.method(#generateSecureKey, []),
          returnValue: <int>[]) as List<int>);
  @override
  _i3.Future<bool> boxExists(String? name) =>
      (super.noSuchMethod(Invocation.method(#boxExists, [name]),
          returnValue: Future<bool>.value(false)) as _i3.Future<bool>);
}

/// A class which mocks [Box].
///
/// See the documentation for Mockito's code generation for more information.
class MockBox<E> extends _i1.Mock implements _i2.Box<E> {
  MockBox() {
    _i1.throwOnMissingStub(this);
  }

  @override
  Iterable<E> get values =>
      (super.noSuchMethod(Invocation.getter(#values), returnValue: [])
          as Iterable<E>);
  @override
  Iterable<E> valuesBetween({dynamic startKey, dynamic endKey}) =>
      (super.noSuchMethod(
          Invocation.method(
              #valuesBetween, [], {#startKey: startKey, #endKey: endKey}),
          returnValue: []) as Iterable<E>);
  @override
  E? getAt(int? index) =>
      (super.noSuchMethod(Invocation.method(#getAt, [index])) as E?);
  @override
  Map<dynamic, E> toMap() => (super.noSuchMethod(Invocation.method(#toMap, []),
      returnValue: <dynamic, E>{}) as Map<dynamic, E>);
}

Here is the error:
image

@srawlins
Copy link
Member

srawlins commented May 3, 2021

Thanks for the bug report. We're going to need more information. Do you get any stack trace? I don't see any API with Future<int> so its hard to say why you get that error from the code you pasted.

@EhiltonKazuo
Copy link
Author

EhiltonKazuo commented May 3, 2021

I think the stack trace is Debug Console in VSCode:

type 'Null' is not a subtype of type 'Future<int>'
MockBox.add
package:hive/…/box/box_base.dart:90
main.<fn>
test\hive_test.dart:40
main.<fn>
test\hive_test.dart:37
2

✖ Should call Hive
Exited (1)

This code occurs when i mock this abstract class:

abstract class Box<E> implements BoxBase<E> {
  // ...
}

I use the add method inside abstract class BoxBase:

abstract class BoxBase<E> {
  // ...

  Future<int> add(E value);

  // ...
}

@srawlins
Copy link
Member

Yes, it looks like the MockBox class does not have an add method, because Box implements BoxBase, rather than extends it. This is fixed with #404

@nazrinharris
Copy link

@srawlins I apologise in advance if I'm just dumb, but I still cant seem to generate the add and putAt methods

I recently updated flutter and dart. And mockito 5.0.8 , hive 2.0.4

I ran flutter pub run build_runner build --delete-conflicting-outputs and generated the following for MockBox :

class MockBox<E> extends _i1.Mock implements _i2.Box<E> {
  MockBox() {
    _i1.throwOnMissingStub(this);
  }

  @override
  Iterable<E> get values =>
      (super.noSuchMethod(Invocation.getter(#values), returnValue: [])
          as Iterable<E>);
  @override
  Iterable<E> valuesBetween({dynamic startKey, dynamic endKey}) =>
      (super.noSuchMethod(
          Invocation.method(
              #valuesBetween, [], {#startKey: startKey, #endKey: endKey}),
          returnValue: []) as Iterable<E>);
  @override
  E? getAt(int? index) =>
      (super.noSuchMethod(Invocation.method(#getAt, [index])) as E?);
  @override
  Map<dynamic, E> toMap() => (super.noSuchMethod(Invocation.method(#toMap, []),
      returnValue: <dynamic, E>{}) as Map<dynamic, E>);
}

I'm still receiving the same error.

type 'Null' is not a subtype of type 'Future<void>'
MockBox.put
package:hive/…/box/box_base.dart:80

For this test:

group('cacheStoraygeUser', () {
    test(
      'should call HiveInterface and Box to cache data',
      () async {
        when(mockHiveInterface.openBox(any)).thenAnswer((_) async => mockBox);
        when(mockBox.put(0, tStoraygeUserModel)).thenAnswer((_) async => {});
        // act
        await dataSourceImpl.cacheStoraygeUser(tStoraygeUserModel);
        // assert
        verify(mockHiveInterface.openBox(STORAYGE_USER_BOX));
        verify(mockBox.put(STORAYGE_USER_ENTRY, tStoraygeUserModel));
      },
    );
  });

Did I miss some sort of setup?

@srawlins
Copy link
Member

No I don't think you're missing anything. The bug still exists in mockito 5.0.8. It should be fixed at HEAD now that #404 has landed. I plan to release a new version tomorrow or Monday.

@nazrinharris
Copy link

Ah okay then. Thanks for clarifying.

@srawlins srawlins closed this as completed Jun 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants