Модель робота для интеграционных тестов Flutter - PullRequest
0 голосов
/ 21 сентября 2018

Чтобы облегчить чтение интеграционных тестов флаттера, я пытаюсь использовать шаблон робота для написания такого кода (цепочка):

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;
});

1 Ответ

0 голосов
/ 22 сентября 2018

Я попытался адаптировать API, который вы хотите, - но для этого я добавил второй класс.Робот в этом случае содержит фактические асинхронные команды, необходимые для взаимодействия с драйвером.Соответствующий класс "Work" содержит класс Future, который он постепенно создает по мере вызова методов.Эти методы в свою очередь вызывают фактическую асинхронную работу над роботом, однако мы можем продолжать вызывать их, не дожидаясь завершения работы робота.В конце мы возвращаем окончательное будущее, которое можно ожидать для всей работы, которая будет завершена.

Обратите внимание, что в этом случае мы не можем использовать синтаксис .., поскольку нам нужно вызвать await для значения, возвращаемого классом Work.

 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;
}
...