用于 Flutter 集成测试的机器人模式
Robot Pattern for Flutter Integration Tests
为了使 flutter 集成测试易于阅读,我尝试使用机器人模式编写如下代码(链接):
test('home', () async {
driver = await FlutterDriver.connect();
HomeRobot(driver)
.seesFromPlacesField()
.seesToPlacesField()
.seesNotSwapButton();
});
基地机器人class长这样:
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;
}
}
但是工作测试目前看起来像这样(然后):
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();
});
关于如何很好地链接它有什么想法吗?
解决方案
机器人搭建 'work':
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提供所有基本步骤:
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;
});
我试图调整您想要的 API - 但是我添加了第二个 class 以便这样做。在这种情况下,机器人包含与驱动程序交互所需的实际异步命令。相应的 "Work" class 包含一个 Future,当您调用它的方法时,它会逐渐建立起来。这些方法依次调用 Robot 上的实际异步工作,但是我们可以继续调用它们而无需等待机器人完成。最后,我们 return 可以等待所有工作完成的最终未来。
请注意,在这种情况下我们不能使用 ..
语法,因为我们需要对 return 由作品 class 编辑的值调用 await
。
final driver = await FlutterDriver.connect();
final robot = HomeRobot(driver);
await robot.start()
.seesFromPlacesField()
.seesToPlacesField()
.seesNotSwapButton()
.work();
class HomeRobot {
HomeRobot(this.driver);
final FlutterDriver driver;
/// Create a new work class which builds up a Future.
HomeRobotWork start() => new HomeRobotWork(this);
Future<void> _seesFromPlacesField() async { ... }
Future<void> _seesToPlacesField() async { ... }
Future<void> _seesNotSwapButton() async { ... }
}
class HomeRobotWork {
HomeRobotWork(this.robot);
final HomeRobot robot;
Future<void> _result = new Future.value(null);
/// Add a future onto the result value
void seesFromPlacesField() {
_result = _result.then((_) => robot._seesFromPlacesField());
return this;
}
void seesToPlacesField() {
_result = _result.then((_) => robot._seesToPlacesField());
return this;
}
void seesNotSwapButton() {
_result = _result.then((_) => robot._seesNotSwapButton());
return this;
}
Future<void> work() => _result;
}
为了使 flutter 集成测试易于阅读,我尝试使用机器人模式编写如下代码(链接):
test('home', () async {
driver = await FlutterDriver.connect();
HomeRobot(driver)
.seesFromPlacesField()
.seesToPlacesField()
.seesNotSwapButton();
});
基地机器人class长这样:
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;
}
}
但是工作测试目前看起来像这样(然后):
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();
});
关于如何很好地链接它有什么想法吗?
解决方案
机器人搭建 'work':
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提供所有基本步骤:
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;
});
我试图调整您想要的 API - 但是我添加了第二个 class 以便这样做。在这种情况下,机器人包含与驱动程序交互所需的实际异步命令。相应的 "Work" class 包含一个 Future,当您调用它的方法时,它会逐渐建立起来。这些方法依次调用 Robot 上的实际异步工作,但是我们可以继续调用它们而无需等待机器人完成。最后,我们 return 可以等待所有工作完成的最终未来。
请注意,在这种情况下我们不能使用 ..
语法,因为我们需要对 return 由作品 class 编辑的值调用 await
。
final driver = await FlutterDriver.connect();
final robot = HomeRobot(driver);
await robot.start()
.seesFromPlacesField()
.seesToPlacesField()
.seesNotSwapButton()
.work();
class HomeRobot {
HomeRobot(this.driver);
final FlutterDriver driver;
/// Create a new work class which builds up a Future.
HomeRobotWork start() => new HomeRobotWork(this);
Future<void> _seesFromPlacesField() async { ... }
Future<void> _seesToPlacesField() async { ... }
Future<void> _seesNotSwapButton() async { ... }
}
class HomeRobotWork {
HomeRobotWork(this.robot);
final HomeRobot robot;
Future<void> _result = new Future.value(null);
/// Add a future onto the result value
void seesFromPlacesField() {
_result = _result.then((_) => robot._seesFromPlacesField());
return this;
}
void seesToPlacesField() {
_result = _result.then((_) => robot._seesToPlacesField());
return this;
}
void seesNotSwapButton() {
_result = _result.then((_) => robot._seesNotSwapButton());
return this;
}
Future<void> work() => _result;
}