如何在 repo 中测试/模拟 Hive (Flutter) 打开框逻辑?

How can I test / mock Hive (Flutter) open box logic in repo?

抱歉,如果这个问题看起来很愚蠢。我正在按照 Rob Martin 的指示学习干净的架构,但我在编写我的一个测试时遇到了一点点麻烦。

我在 Hive 存储库中编写了几个函数。这是代码

import 'package:hive/hive.dart';
import 'package:movie_browser/features/SearchMovie/domain/entities/movie_detailed_entity.dart';

abstract class HiveMovieSearchRepoAbstract {
  Future<void> cacheMovieDetails(MovieDetailed movie);
  Future<MovieDetailed> getCachedMovieDetails(String id);
}

// const vars to prevent misspellings
const String MOVIEDETAILSBOX = "MovieDetailedBox";
const String SEARCHBOX = "SearchBox";

class HiveMovieSearchRepo implements HiveMovieSearchRepoAbstract {
  Box movieDetailsBox = Hive.box(MOVIEDETAILSBOX) ?? null;
  // TODO implement searchbox
  // final searchBox = Hive.box(SEARCHBOX);

  Future<void> cacheMovieDetails(MovieDetailed movie) async {
    /// expects a MovieDetailed to cache.  Will cache that movie
    movieDetailsBox ?? await _openBox(movieDetailsBox, MOVIEDETAILSBOX);

    movieDetailsBox.put('${movie.id}', movie);
  }

  Future<MovieDetailed> getCachedMovieDetails(String id) async {
    /// expects a string id as input
    /// returns the MovieDetailed if cached previously
    /// returns null otherwise
    movieDetailsBox ?? await _openBox(movieDetailsBox, MOVIEDETAILSBOX);

    return await movieDetailsBox.get('$id');
  }

  _openBox(Box box, String type) async {
    await Hive.openBox(type);
    return Hive.box(type);
  }
}

我想不出如何测试这个?我想要两种情况,一种箱子已经打开,一种箱子没有。

具体来说,我要测试的是这些行

movieDetailsBox ?? await _openBox(movieDetailsBox, MOVIEDETAILSBOX);

_openBox(Box box, String type) async {
    await Hive.openBox(type);
    return Hive.box(type);
  }

我考虑过模拟 Box 对象然后做类似的事情......

when(mockHiveMovieSearchRepo.getCachedMovieDetails(some_id)).thenAnswer((_) async => object)

但这不会绕过我要测试的代码并始终显示为阳性吗?

非常感谢您的帮助

我不知道我是否完全理解你的问题,但你可以尝试这样的事情

abstract class HiveMovieSearchRepoAbstract {
  Future<void> cacheMovieDetails(MovieDetailed movie);
  Future<MovieDetailed> getCachedMovieDetails(String id);
}

// const vars to prevent misspellings
const String MOVIEDETAILSBOX = "MovieDetailedBox";
const String SEARCHBOX = "SearchBox";

class HiveMovieSearchRepo implements HiveMovieSearchRepoAbstract {
  final HiveInterface hive;

  HiveMovieSearchRepo({@required this.hive});

  @override
  Future<void> cacheMovieDetails(MovieDetailed cacheMovieDetails) async {
    /// expects a MovieDetailed to cache.  Will cache that movie
    try {
      final moviedetailbox = await _openBox(MOVIEDETAILSBOX);
      moviedetailbox.put('${movie.id}', movie);
    } catch (e) {
      throw CacheException();
    }
  }

  Future<MovieDetailed> getCachedMovieDetails(String id) async {
    /// expects a string id as input
    /// returns the MovieDetailed if cached previously
    /// returns null otherwise
    try {
      final moviedetailbox = await _openBox(MOVIEDETAILSBOX);
      if (moviedetailbox.containsKey(boxkeyname)) {
        return await movieDetailsBox.get('$id');
      }
      return null;
    } catch (e) {
      return CacheException();
    }
  }

  Future<Box> _openBox(String type) async {
    try {
      final box = await hive.openBox(type);
      return box;
    } catch (e) {
      throw CacheException();
    }
  }
}

要测试它,您可以这样做

class MockHiveInterface extends Mock implements HiveInterface {}

class MockHiveBox extends Mock implements Box {}

void main() {
  MockHiveInterface mockHiveInterface;
  MockHiveBox mockHiveBox;
  HiveMovieSearchRepo hiveMovieSearchRepo;
  setUp(() {
    mockHiveInterface = MockHiveInterface();
    mockHiveBox = MockHiveBox();
    hiveMovieSearchRepo = HiveMovieSearchRepo(hive: mockHiveInterface);
  });

  group('cacheMoviedetails', () {

    
    test(
    'should cache the movie details',
     () async{
        //arrange
         when(mockHiveInterface.openBox(any)).thenAnswer((_) async => mockHiveBox);
        //act
      await hiveMovieSearchRepo.cacheMovieDetails(tcacheMovieDetails);
        //assert
      verify(mockHiveBox.put('${movie.id}', tmovie));
      verify(mockHiveInterface.openBox("MovieDetailedBox"));
      });
    
  });

  group('getLocalCitiesAndCountriesAtPage', () {
    test('should when', () async {
      //arrange
      when(mockHiveInterface.openBox(any))
          .thenAnswer((realInvocation) async => mockHiveBox);
      when(mockHiveBox.get('$id'))
          .thenAnswer((realInvocation) async => tmoviedetails);
      //act
      final result =
          await hiveMovieSearchRepo.getCachedMovieDetails(tId);
      //assert
      verify(mockHiveInterface.openBox(any));
      verify(mockHiveBox.get('page${tpage.toString()}'));
      expect(result, tmoviedetails);
    });
  });

}

您还应该为 CacheExeption() 添加一些测试。 希望对你有帮助。

所以,我写这个 post 9 个月。 Whosebug 刚刚给我发了一条通知,说这是一个热门问题,所以我会为其他想知道同样问题的人回答这个问题

使此可测试的简单方法是将 Box 更改为传递到 class 的 arg,就像这样

abstract class ClassName {
  final Box movieDetailsBox;
  final Box searchBox;

  ClassName({
    this.moveDetailsBox,
    this.searchBox,
  });
}

这使得盒子可模拟和可测试

你应该模拟蜂巢界面和盒子;