【问题标题】:Flutter Mockito Test Mock FailureFlutter Mockito 测试模拟失败
【发布时间】:2021-12-25 09:48:58
【问题描述】:

我试图在我的 Flutter Widget 测试用例中模拟一个存储库,但每次运行测试时都会遇到如下所示的错误。

package:quiz_test/level/repository/LevelRepository.dart 6:23   MockLevelRepository.fetchLevels
test/unit/level/view_model/LevelSelectionViewModel.dart 46:30  main.<fn>
test/unit/level/view_model/LevelSelectionViewModel.dart 32:38  main.<fn>

type 'Null' is not a subtype of type 'Future<List<Level>>'

这是我的测试用例(我稍后会改进测试名称)。

  test('Waiting for an interaction', () async {
    final mockLevelRepository = MockLevelRepository();

    List<Level> levelsList = <Level>[];

    Level level = Level(id: 1, name: "Level 1");
    levelsList.add(level);

    level = Level(id: 2, name: "Level 2");
    levelsList.add(level);

    level = Level(id: 3, name: "Level 3");
    levelsList.add(level);

    when(mockLevelRepository.fetchLevels())
        .thenAnswer((_) async => Future.value(levelsList));

    LevelSelectionViewModel levelSelectionViewModel =
        new LevelSelectionViewModel();
    levelSelectionViewModel.setRepository(mockLevelRepository);

    levelSelectionViewModel.fetchLevels();
  });

我很感激此时我不是在“测试”任何东西,我只是想让它按原样通过。话虽如此,我也不确定如何处理这个问题,因为它会返回一个未来。

可以看出我已经模拟了存储库。如果我在levelSelectionViewModel.fetchLevels(); 行上放一个断点,它永远不会被命中。

整个文件是:

LevelSelectionViewModelTest

import 'package:flutter/material.dart';
import 'package:mockito/mockito.dart';
import 'package:quiz_test/level/repository/LevelRepository.dart';
import 'package:quiz_test/level/view_model/LevelSelectionViewModel.dart';
import 'package:quiz_test/models/Level.dart';
import 'package:test/test.dart';

class MockLevelRepository extends Mock implements LevelRepository {}

void main() {
  test('Test isLevelLocked returns correct response', () {
    bool response = LevelSelectionViewModel().isLevelLocked(1);
    expect(response, false);

    response = LevelSelectionViewModel().isLevelLocked(2);
    expect(response, false);

    response = LevelSelectionViewModel().isLevelLocked(3);
    expect(response, false);

    response = LevelSelectionViewModel().isLevelLocked(0);
    expect(response, true);

    response = LevelSelectionViewModel().isLevelLocked(4);
    expect(response, true);
  });

  test('Waiting for an interaction', () async {
    final mockLevelRepository = MockLevelRepository();

    List<Level> levelsList = <Level>[];

    Level level = Level(id: 1, name: "Level 1");
    levelsList.add(level);

    level = Level(id: 2, name: "Level 2");
    levelsList.add(level);

    level = Level(id: 3, name: "Level 3");
    levelsList.add(level);

    when(mockLevelRepository.fetchLevels())
        .thenAnswer((_) async => Future.value(levelsList));

    LevelSelectionViewModel levelSelectionViewModel =
        new LevelSelectionViewModel();
    levelSelectionViewModel.setRepository(mockLevelRepository);

    levelSelectionViewModel.fetchLevels();
  });
}

LevelRepository

import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:quiz_test/models/Level.dart';

class LevelRepository {
  Future<List<Level>> fetchLevels() async {
    final jsondata =
        await rootBundle.loadString('assets/data/levels/Levels.json');

    final body = jsonDecode(jsondata);
    final Iterable json = body["Items"];
    return json.map((level) => Level.fromJson(level)).toList();
  }
}

LevelSelectionViewModel

import 'package:flutter/material.dart';
import 'package:quiz_test/level/repository/LevelRepository.dart';
import '../../models/Levels.dart';

class LevelSelectionViewModel extends ChangeNotifier {
  late LevelRepository levelRepository;

  // ToDo - Need to identify how to watch 'levels' instead.
  bool changed = false;
  List<Levels> levels = <Levels>[];

  void setRepository(LevelRepository levelRepository) {
    this.levelRepository = levelRepository;
  }

  Future<void> fetchLevels() async {
    final results = await levelRepository.fetchLevels();
    this.levels = results.map((item) => Levels(level: item)).toList();
    changed = true;
    notifyListeners();
  }

  // ToDo - These are hard coded for now... will change later.
  bool isLevelLocked(int levelNumber) {
    if (levelNumber >= 1 && levelNumber <= 3) return false;
    return true;
  }
}

注意: 我试过添加 @GenerateMocks([LevelSelectionViewModel, LevelRepository])

就在我的测试文件中的 main() 之前,但这并没有解决我的问题。

所以我这里有两个问题:

a) 如何修复错误?我不明白问题是什么。我读到它可能与可空性有关,但我不确定“为什么”这是一个问题。另一个潜在问题是类型错误,但我认为这也不是问题所在。

b) 由于fetchLevels 调用返回一个Future,我应该如何测试这个方法是否正在做它应该做的事情。最终,我认为我可能需要“观看”才能收到通知。

值得注意的是,我正在使用 get_in 和 get_in_mixer。我正在使用 Mockito 进行测试。

非常感谢任何帮助。

【问题讨论】:

    标签: flutter unit-testing mockito flutter-test


    【解决方案1】:

    所以,我现在可以回答第一部分了。简而言之,我应该更多地关注 Mockito 文档,因为答案都在那里。

    a) 我添加了@GenerateMocks 注释

    @GenerateMocks([LevelRepository])
    void main() {
      test('Test isLevelLocked returns correct response', () {
        bool response = LevelSelectionViewModel().isLevelLocked(1);
    

    b) 我在 pubspec.yml 中添加了build_runner: ^2.1.5

    c) 我跑了flutter pub get

    d) 我跑了flutter packages pub run build_runner build

    e) 我将新的导入 import 'LevelSelectionViewModel.mocks.dart'; 添加到我的测试文件中。

    然后测试运行完成。现在剩下的就是让我了解如何测试这个未来。

    我通过执行以下操作成功地测试了未来。分享,希望这可以帮助其他可能被卡住的人!

    import 'package:flutter/foundation.dart';
    import 'package:mockito/annotations.dart';
    import 'package:mockito/mockito.dart';
    import 'package:quiz_test/level/repository/LevelRepository.dart';
    import 'package:quiz_test/level/view_model/LevelSelectionViewModel.dart';
    import 'package:quiz_test/models/Level.dart';
    import 'package:test/test.dart';
    
    import 'LevelSelectionViewModel.mocks.dart';
    
    @GenerateMocks([LevelRepository])
    void main() {
      test('Test that LevelSelectionViewModel level is correctly set', () async {
        var mockLevelRepository = MockLevelRepository();
    
        List<Level> levelsList = <Level>[];
    
        Level level = Level(id: 1, name: "Level 1");
        levelsList.add(level);
    
        level = Level(id: 2, name: "Level 2");
        levelsList.add(level);
    
        level = Level(id: 3, name: "Level 3");
        levelsList.add(level);
    
        when(mockLevelRepository.fetchLevels())
            .thenAnswer((_) async => SynchronousFuture((levelsList)));
    
        LevelSelectionViewModel levelSelectionViewModel =
            new LevelSelectionViewModel();
        levelSelectionViewModel.setRepository(mockLevelRepository);
    
        await levelSelectionViewModel.fetchLevels();
    
        expect(levelSelectionViewModel.levels.length, 3);
      });
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-07
      • 1970-01-01
      • 1970-01-01
      • 2020-01-05
      • 1970-01-01
      • 2022-06-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多