Я столкнулся с интересной проблемой во время написания некоторых юнит-тестов для моего проекта.
Вот карта, которую пользователь может использовать для размещения маркеров:
class DomainMap {
static const _DEFAULT_COORDINATE = const Coordinate(40.73, -73.93);
final ReverseGeocodingStrategy _geocodingStrategy;
final RouteDefinitionStrategy _assemblyStrategy;
final List<_IdentifiedCoordinate> _addressed = [];
final List<Coordinate> _markers = [];
final _Route _route = _Route();
Coordinate get defaultCoordinate => _DEFAULT_COORDINATE;
DomainMap(this._geocodingStrategy, this._assemblyStrategy);
Stream<MarkersUpdateEvent> mark(Coordinate coordinate) async* {
_markers.add(coordinate);
yield _assembleMarkersUpdate();
final Address address = await _geocodingStrategy.geocode(coordinate);
_addressed.add(_IdentifiedCoordinate(coordinate, address));
if (_addressed.length > 1) {
final Iterable<Coordinate> assembledPolyline =
await _assemblyStrategy.buildRoute(BuiltList(_addressed
.map((identifiedCoordinate) => identifiedCoordinate.address)));
assembledPolyline.forEach(_route.add);
yield _assembleMarkersUpdate();
}
}
MarkersUpdateEvent _assembleMarkersUpdate() =>
MarkersUpdateEvent(BuiltList.from(_markers), _route.readOnly);
}
class _Route {
final List<Coordinate> _points = [];
Iterable<Coordinate> get readOnly => BuiltList(_points);
void add(final Coordinate coordinate) => _points.add(coordinate);
void addAll(final Iterable<Coordinate> coordinate) => _points.addAll(coordinate);
}
А вот блокпроверьте, проверяет ли он, что на второй отметке должен быть возвращен маршрут:
test("mark, assert that on second mark at first just markers update is published, and then the polyline update too", () async {
final Coordinate secondCoordinate = plus(givenCoordinate, 1);
final givenRoute = [
givenCoordinate,
minus(givenCoordinate, 1),
plus(givenCoordinate, 1)
];
when(geocodingStrategy.geocode(any)).thenAnswer((invocation) => Future.value(Address(invocation.positionalArguments[0].toString())));
when(assemblyStrategy.buildRoute(any))
.thenAnswer((_) => Future.value(givenRoute));
final expectedFirstUpdate =
MarkersUpdateEvent([givenCoordinate, secondCoordinate], []);
final expectedSecondUpdate =
MarkersUpdateEvent([givenCoordinate, secondCoordinate], givenRoute);
final DomainMap map = domainMap();
map.mark(givenCoordinate)
//.forEach(print) //Important
;
expect(map.mark(secondCoordinate),
emitsInOrder([expectedFirstUpdate, expectedSecondUpdate]));
}, timeout: const Timeout(const Duration(seconds: 10)));
Когда я запускаю его таким образом, проверка завершается неудачно и говорит, что потоку было передано только одно значение - событие обновления столько markers
поле не пустое, которое содержит только secondCoordinate
.Но когда я раскомментирую forEach
, тогда тест проходит.
Насколько я понимаю, метод async*
не вызывается, пока не будут запрошены значения потока, поэтому, когда вызывается forEach
, - функцияисполняется до конца.Поэтому, если я запрашиваю все значения потока (которые были возвращены при первом вызове) - метод будет выполнен, список заполнен markers
, и второе выполнение будет выполнено в ожидаемом состоянии.
Я понимаю async*
семантика правильно?И есть ли способ сделать эту функцию активной, а не ленивой (я не хочу запрашивать ненужные значения потока)?