简体   繁体   English

Flutter Mockito 测试模拟失败

[英]Flutter Mockito Test Mock Failure

I am trying to mock a repository in my Flutter Widget test case but each time I run the test I encounter an error as shown below.我试图在我的 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>>'

This is my test case (I will improve the test name later).这是我的测试用例(稍后我会改进测试名称)。

  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();
  });

I appreciate at this point I'm not 'testing' for anything, I just want to get it to pass as is.我很感激在这一点上我不是在“测试”任何东西,我只是想让它按原样通过。 Having said that I'm not sure either how to handle this since it returns a future.话虽如此,我不确定如何处理这个问题,因为它返回了一个未来。

As can be seen I've mocked the repository.可以看出我已经模拟了存储库。 If I put a breakpoint on the line levelSelectionViewModel.fetchLevels();如果我在levelSelectionViewModel.fetchLevels();行上放了一个断点levelSelectionViewModel.fetchLevels(); it is never hit.它永远不会被击中。

The entire file is:整个文件是:

LevelSelectionViewModelTest 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;
  }
}

Note: I have tried adding @GenerateMocks([LevelSelectionViewModel, LevelRepository])注意:我尝试添加 @GenerateMocks([LevelSelectionViewModel, LevelRepository])

just before main() in my test file but that did not resolve my issue.就在我的测试文件中的 main() 之前,但这并没有解决我的问题。

So I have two issues here:所以我在这里有两个问题:

a) How do I fix the error? a) 如何修复错误? I can't understand what the problem is.我无法理解问题是什么。 I read that it could be about nullability but I'm not sure 'why' that is an issue here.我读到它可能与可空性有关,但我不确定“为什么”这是一个问题。 The other potential issue was the type was wrong but I don't see that that's the problem here either.另一个潜在问题是类型错误,但我也不认为这是这里的问题。

b) Since the fetchLevels call returns a Future, how should I test that this method is doing what it should. b) 由于fetchLevels调用返回一个 Future,我应该如何测试这个方法正在做它应该做的事情。 Ultimately I think I'd need to perhaps 'watch' to be notified.最终,我认为我可能需要“观看”才能收到通知。

It's worth noting that I am using get_in and get_in_mixer.值得注意的是,我正在使用 get_in 和 get_in_mixer。 I am testing using Mockito.我正在使用 Mockito 进行测试。

Any help is very much appreciated.很感谢任何形式的帮助。

So, I can now answer the first part of this.所以,我现在可以回答这个问题的第一部分。 In short I should have paid more attention to the Mockito documentation as the answers were all there.简而言之,我应该更加关注 Mockito 文档,因为答案都在那里。

a) I added the @GenerateMocks annotation a) 我添加了@GenerateMocks注释

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

b) I added build_runner: ^2.1.5 to pubspec.yml b) 我将build_runner: ^2.1.5添加到 pubspec.yml

c) I ran flutter pub get c) 我跑了flutter pub get

d) I ran flutter packages pub run build_runner build d) 我运行flutter packages pub run build_runner build

e) I added the new import import 'LevelSelectionViewModel.mocks.dart'; e) 我添加了新的 import import 'LevelSelectionViewModel.mocks.dart'; to my test file.到我的测试文件。

The test then ran to completion.然后测试运行完成。 All that's left now is for me to understand how to test this future.现在剩下的就是让我了解如何测试这个未来。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM