Чтобы облегчить чтение интеграционных тестов флаттера, я пытаюсь использовать шаблон робота для написания такого кода (цепочка):
test('home', () async {
driver = await FlutterDriver.connect();
HomeRobot(driver)
.seesFromPlacesField()
.seesToPlacesField()
.seesNotSwapButton();
});
Базовый класс робота выглядит следующим образом:
class RobotBase {
RobotBase(this.driver, this.finder);
final FlutterDriver driver;
final SerializableFinder finder;
sees(String valueKey) async {
print("sees: $valueKey");
await driver.waitFor(find.byValueKey(valueKey));
}
seesNot(String valueKey) async {
print("sees not: $valueKey");
await driver.waitForAbsent(find.byValueKey(valueKey));
}
}
И настоящий робот, подобный этому:
class HomeRobot extends Robot {
HomeRobot(FlutterDriver driver)
: super(driver, find.byValueKey(home_keys.page));
Future<HomeRobot> seesFromPlacesField() async {
await sees(home_keys.fromPlaceField);
return this;
}
Future<HomeRobot> seesToPlacesField() async {
await sees(home_keys.toPlaceField);
return this;
}
Future<HomeRobot> seesNotSwapButton() async {
await seesNot(home_keys.swapButton);
return this;
}
Future<HomeRobot> seesSwapButton() async {
await sees(home_keys.swapButton);
return this;
}
}
Но рабочий тест в настоящее время выглядит следующим образом (thening):
test('home', () async {
driver = await FlutterDriver.connect();
HomeRobot r = HomeRobot(driver);
await r.seesFromPlacesField().then((r) async {
await r.seesToPlacesField().then((r) async {
await r.seesNotSwapButton();
});
});
});
Или вот это (в ожидании):
test('home', () async {
driver = await FlutterDriver.connect();
HomeRobot r = HomeRobot(driver);
r = await (await (await r
.seesFromPlacesField())
.seesToPlacesField())
.seesNotSwapButton();
});
Есть какие-нибудь идеи о том, как это красиво организовать?
Решение
Роботы создают «работу»:
class HomeRobot extends Robot {
HomeRobot(FlutterDriver driver, Future<void> work) : super(driver, work);
HomeRobot seesFromPlacesField() {
work = work.then((_) async => await seesKey(keys.homePageFromPlaceField));
return this;
}
SearchRobot tapsOnFromPlacesField() {
work = work.then((_) async => await tapsOnKey(keys.homePageFromPlaceField));
return SearchRobot(driver, work);
}
// ...
}
class SearchRobot extends Robot {
SearchRobot(FlutterDriver driver, Future<void> work) : super(driver, work);
SearchRobot seesBackButton() {
work = work.then((_) async => await seesTooltip(Robot.backButtonTooltip));
return this;
}
HomeRobot tapsOnBackButton() {
work = work.then((_) async => await tapsOnTooltip(Robot.backButtonTooltip));
return HomeRobot(driver, work);
}
// ...
}
Базовый класс обеспечивает все основные шаги:
class Robot {
Robot(this.driver, this.work);
final FlutterDriver driver;
Future<void> work;
seesKey(String key) async {
print("sees key: $key");
await driver.waitFor(find.byValueKey(key));
}
tapsOnKey(String key) async {
print("taps on key: $key");
await driver.tap(find.byValueKey(key));
}
// ...
}
И тестовый пример выглядит так:
test('home', () async {
HomeRobot robot = HomeRobot(driver, Future.value(null));
await robot
.seesFromPlacesField()
.seesToPlacesField()
.seesNotSwapButton()
.tapsOnFromPlacesField()
.seesSearchField()
.seesBackButton()
.tapsOnBackButton()
.seesFromPlacesField()
.work;
});