如何对从函数返回的流进行单元测试?

How to unit test a stream that is returned from a function?

我有以下设置:

class Resource<T> {
  T? data;
  String? error;

  Resource._({this.data, this.error});
  factory Resource.success(T? data) => Resource._(data: data);
  factory Resource.error(String error) => Resource._(error: error);
  factory Resource.loading() => Resource._(); 
}

 class CategoriesRepo{
    Stream<Resource<List<Category>>> getAllCategories() async* {
        yield Resource.loading();
        yield (Resource.error(NetworkErrors.NO_INTERNET));
      }
}

test('Loading then error',
      () async {
    final categoriesRepo = CategoriesRepo();
    expect(
        categoriesRepo.getAllCategories(),
        emitsInOrder([
          Resource<List<Category>>.loading(),
          Resource<List<Category>>.error(""),
        ]));
  });

我收到这个错误:

test\unit-tests\screens\categories\categories_repo_test_test.dart       main.<fn>.<fn>

Expected: should emit an event that <Instance of 'Resource<List<Category>>'>
  Actual: <Instance of '_ControllerStream<Resource<List<Category>>>'>
   Which: emitted * Instance of 'Resource<List<Category>>'
                  * Instance of 'Resource<List<Category>>'
                  x Stream closed.

如何正确测试上述流?

测试本身是有效的,但是在对象的断言和比较方面有一个小问题。基本上 Resource.loading() == Resource.loading() 是假的,所以断言失败。

为什么是假的?默认情况下,Dart 对象(除了基元)只有在 相同的实例 .

时才相等

要使您的断言和此类测试有效,您需要为您的对象实施 == 运算符和 hashCode。您可以手动完成,但这有点低效。很多人使用包 equatable or freezed, equatable is a bit easier as it doesn't involve code generation and is recommended by bloc(基于流的状态管理)作者。

import 'package:equatable/equatable.dart';

class Resource<T> extends Equatable {
  T? data;
  String? error;

  Resource._({this.data, this.error});
  factory Resource.success(T? data) => Resource._(data: data);
  factory Resource.error(String error) => Resource._(error: error);
  factory Resource.loading() => Resource._();

  @override
  List<Object?> get props => [data, error];
}

当然你也可以只改变断言,这样它就不再使用对象的比较,而是使用谓词和匹配器,但这并不漂亮。

expect(
  categoriesRepo.getAllCategories(),
  emitsInOrder(
    [
      predicate<Resource<List<Category>>>(
          (r) => r.error == null && r.data == null),
      predicate<Resource<List<Category>>>(
          (r) => r.error == "" && r.data == null),
    ],
  ),
);