我如何为 Flutter 方法编写一个单元测试,稍后用 future 完成?

How do I write a unit test for a Flutter method that completes later with a future?

我正在为调用异步方法的 Flutter 方法编写单元测试,然后 returns,让异步在何时完成。我的测试失败 "after it had already completed".

这是我的测试:

    test('mark as viewed', () {
      final a = Asset();
      expect(a.viewed, false);
      a.markAsViewed();
      expect(a.viewed, true);
    });

这是它正在测试的方法:

  void markAsViewed() {
    viewed = true;
    Repository.get().saveToStorage();
  }

saveToStorage() 方法是一个异步方法,我只是让它在后台执行。

我该如何进行这项工作?测试失败告诉我 Make sure to use [expectAsync] or the [completes] matcher when testing async code. 但我不知道该怎么做。谁能解释一下,或者请给我指出正确的文档?当返回的不是 Future 而只是单独完成时,我找不到有关如何处理这些异步的任何信息。

要明确 - 这个单元测试不是关于测试它是否被保存到存储,只是一个基本的测试设置 viewedtrue

已编辑

错误如下:

package:flutter/src/services/platform_channel.dart 319:7  MethodChannel.invokeMethod
===== asynchronous gap ===========================
dart:async                                                _asyncErrorWrapperHelper
package:exec_pointers/asset_details.dart                  Repository.saveToStorage
package:exec_pointers/asset_details.dart 64:22            Asset.markAsViewed
test/asset_details_test.dart 57:9                         main.<fn>.<fn>
This test failed after it had already completed. Make sure to use [expectAsync]
or the [completes] matcher when testing async code.

此代码与实现问题紧密耦合,这使得对其进行孤立测试变得困难。

应该重构它以遵循更 SOLID 的设计,该设计具有显式依赖关系,可以在隔离测试(单元测试)时替换

例如

class Asset {
    Asset({Repository repository}) {
        this.repository = repository;
    }

    final Repository repository;
    bool viewed;

    void markAsViewed() {
        viewed = true;
        repository.saveToStorage();
    }

    //...
}

这种方式在测试 mock/stub 依赖项时可用于避免任何不需要的行为。

// Create a Mock Repository using the Mock class provided by the Mockito package.
// Create new instances of this class in each test.
class MockRepository extends Mock implements Repository {}

main() {
  test('mark as viewed', () {
    final repo = MockRepository();
    // Use Mockito to do nothing when it calls the repository
    when(repo.saveToStorage())
      .thenAnswer((_) async => { });

    final subject = Asset(repo);
    expect(subject.viewed, false);
    subject.markAsViewed();
    expect(subject.viewed, true);
    //
    verify(repo.saveToStorage());
  });
}

现在应该可以执行测试,而不会出现依赖项的意外行为。

引用An introduction to unit testing
参考Mock dependencies using Mockito
参考mockito 4.1.1