feat(debug): added ability to run tests against an already running app

This commit is contained in:
Jon Samwell 2020-03-25 11:40:42 +11:00
parent a3bebfdedc
commit ba28cc8f30
7 changed files with 82 additions and 24 deletions

View File

@ -1,3 +1,6 @@
## [1.1.7+7] - 25/03/2019
* Added the ability to test against an already running app; enabling you to debug a running application while it has tests executed against it. Setting the configuration property `runningAppProtocolEndpointUri` to the service protocol endpoint (found in stdout when an app has `--verbose` logging turrned on) will ensure that the existing app is connected to rather than starting a new instance of the app. NOTE: ensure the app you are trying to connect to calls `enableFlutterDriverExtension()` when it starts up otherwise the Flutter Driver will not be able to connect to it.
## [1.1.7+6] - 04/03/2019
* Updated to latest Gherkin library (see https://github.com/jonsamwell/dart_gherkin/blob/master/CHANGELOG.md#117---04032020) - this includes a breaking change to the `Hook` inteface that will need to be updated if any of the `Scenerio` level methods are implemented
* Ensured the well known step `I tap the ".." button` scroll the element into view first

View File

@ -75,6 +75,7 @@ NOTE: If you are using a Flutter branch other than the current stable version 1.
- [Pre-defined Steps](#pre-defined-steps)
- [Flutter Driver Utilities](#flutter-driver-utilities)
- [Debugging](#debugging)
- [Debugging the app under test](#debugging-the-app-under-test)
<!-- /TOC -->
@ -488,6 +489,12 @@ Defaults to empty string
This optional argument lets you specify device target id as `flutter run --device-id` command. To show list of connected devices, run `flutter devices`. If you only have one device connected, no need to provide this argument.
#### runningAppProtocolEndpointUri
An observatory url that the test runner can connect to instead of creating a new running instance of the target application
The url takes the form of `http://127.0.0.1:51540/EM72VtRsUV0=/` and usually printed to stdout in the form `Connecting to service protocol: http://127.0.0.1:51540/EM72VtRsUV0=/`
You will have to add the `--verbose` flag to the command to start your flutter app to see this output and ensure `enableFlutterDriverExtension()` is called by the running app
## Features Files
### Steps Definitions
@ -971,3 +978,11 @@ After which the file will most likely look like this
]
}
```
#### Debugging the app under test
Setting the configuration property `runningAppProtocolEndpointUri` to the service protocol endpoint (found in stdout when an app has `--verbose` logging turrned on) will ensure that the existing app is connected to rather than starting a new instance of the app.
NOTE: ensure the app you are trying to connect to calls `enableFlutterDriverExtension()` when it starts up otherwise the Flutter Driver will not be able to connect to it.
Also ensure that the `--verbose` flag is set when starting the app to test, this will then log the service protocol endpoint out to the console which is the uri you will need to set this property to. It usually takes the form of `Connecting to service protocol: http://127.0.0.1:51540/EM72VtRsUV0=/` so set the `runningAppProtocolEndpointUri` to `http://127.0.0.1:51540/EM72VtRsUV0=/` and then start the tests.

View File

@ -31,7 +31,7 @@ Future<void> main() {
..customStepParameterDefinitions = [
ColourParameter(),
]
..restartAppBetweenScenarios = true
..restartAppBetweenScenarios = false
..targetAppWorkingDirecotry = "../"
..targetAppPath = "test_driver/app.dart"
// ..buildFlavor = "staging" // uncomment when using build flavor and check android/ios flavor setup see android file android\app\build.gradle
@ -40,6 +40,8 @@ Future<void> main() {
// ..logFlutterProcessOutput = true // uncomment to see command invoked to start the flutter test app
// ..verboseFlutterProcessLogs = true // uncomment to see the verbose output from the Flutter process
// ..flutterBuildTimeout = Duration(minutes: 3) // uncomment to change the default period that flutter is expected to build and start the app within
..runningAppProtocolEndpointUri =
'http://127.0.0.1:51540/bkegoer6eH8=/' // already running app observatory / service protocol uri (with enableFlutterDriverExtension method invoked) to test against if you use this set `restartAppBetweenScenarios` to false
..exitAfterTestRun = true; // set to false if debugging to exit cleanly
return GherkinRunner().execute(config);
}

View File

@ -66,6 +66,11 @@ class FlutterTestConfiguration extends TestConfiguration {
/// Defaults to 3
int flutterDriverMaxConnectionAttemps = 3;
/// An observatory url that the test runner can connect to instead of creating a new running instance of the target application
/// Url takes the form of `http://127.0.0.1:51540/EM72VtRsUV0=/` and usually printed to stdout in the form `Connecting to service protocol: http://127.0.0.1:51540/EM72VtRsUV0=/`
/// You will have to add the `--verbose` flag to the command to start your flutter app to see this output and ensure `enableFlutterDriverExtension()` is called by the running app
String runningAppProtocolEndpointUri;
void setObservatoryDebuggerUri(String uri) => _observatoryDebuggerUri = uri;
Future<FlutterDriver> createFlutterDriver([String dartVmServiceUrl]) async {
@ -76,15 +81,26 @@ class FlutterTestConfiguration extends TestConfiguration {
}
Future<FlutterWorld> createFlutterWorld(
TestConfiguration config, FlutterWorld world) async {
TestConfiguration config,
FlutterWorld world,
) async {
FlutterTestConfiguration flutterConfig = config as FlutterTestConfiguration;
world = world ?? FlutterWorld();
final driver = await createFlutterDriver();
final driver = await createFlutterDriver(
flutterConfig.runningAppProtocolEndpointUri != null &&
flutterConfig.runningAppProtocolEndpointUri.isNotEmpty
? flutterConfig.runningAppProtocolEndpointUri
: null,
);
world.setFlutterDriver(driver);
return world;
}
@override
void prepare() {
_ensureCorrectConfiguration();
final providedCreateWorld = createWorld;
createWorld = (config) async {
FlutterWorld world;
@ -133,4 +149,19 @@ class FlutterTestConfiguration extends TestConfiguration {
}
}
}
void _ensureCorrectConfiguration() {
if (runningAppProtocolEndpointUri != null &&
runningAppProtocolEndpointUri.isNotEmpty) {
if (restartAppBetweenScenarios) {
throw AssertionError(
'Cannot restart app between scenarios if using runningAppProtocolEndpointUri');
}
if (targetDeviceId != null && targetDeviceId.isNotEmpty) {
throw AssertionError(
'Cannot target specific device id if using runningAppProtocolEndpointUri');
}
}
}
}

View File

@ -61,22 +61,29 @@ class FlutterAppRunnerHook extends Hook {
}
Future<void> _runApp(FlutterTestConfiguration config) async {
_flutterRunProcessHandler = FlutterRunProcessHandler()
..setLogFlutterProcessOutput(config.logFlutterProcessOutput)
..setVerboseFluterlogs(config.verboseFlutterProcessLogs)
..setApplicationTargetFile(config.targetAppPath)
..setDriverConnectionDelay(config.flutterDriverReconnectionDelay)
..setWorkingDirectory(config.targetAppWorkingDirecotry)
..setBuildRequired(haveRunFirstScenario ? false : config.build)
..setBuildFlavor(config.buildFlavor)
..setDeviceTargetId(config.targetDeviceId);
if (config.runningAppProtocolEndpointUri != null &&
config.runningAppProtocolEndpointUri.isNotEmpty) {
stdout.writeln(
"Connecting to running Flutter app under test at '${config.runningAppProtocolEndpointUri}', this might take a few moments");
config.setObservatoryDebuggerUri(config.runningAppProtocolEndpointUri);
} else {
_flutterRunProcessHandler = FlutterRunProcessHandler()
..setLogFlutterProcessOutput(config.logFlutterProcessOutput)
..setVerboseFluterlogs(config.verboseFlutterProcessLogs)
..setApplicationTargetFile(config.targetAppPath)
..setDriverConnectionDelay(config.flutterDriverReconnectionDelay)
..setWorkingDirectory(config.targetAppWorkingDirecotry)
..setBuildRequired(haveRunFirstScenario ? false : config.build)
..setBuildFlavor(config.buildFlavor)
..setDeviceTargetId(config.targetDeviceId);
stdout.writeln(
"Starting Flutter app under test '${config.targetAppPath}', this might take a few moments");
await _flutterRunProcessHandler.run();
final observatoryUri = await _flutterRunProcessHandler
.waitForObservatoryDebuggerUri(config.flutterBuildTimeout);
config.setObservatoryDebuggerUri(observatoryUri);
stdout.writeln(
"Starting Flutter app under test '${config.targetAppPath}', this might take a few moments");
await _flutterRunProcessHandler.run();
final observatoryUri = await _flutterRunProcessHandler
.waitForObservatoryDebuggerUri(config.flutterBuildTimeout);
config.setObservatoryDebuggerUri(observatoryUri);
}
}
Future<void> _terminateApp() async {

View File

@ -70,7 +70,7 @@ packages:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.8"
version: "0.13.9"
crypto:
dependency: transitive
description:
@ -153,7 +153,7 @@ packages:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.3"
version: "3.1.4"
image:
dependency: transitive
description:
@ -251,7 +251,7 @@ packages:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.1"
version: "1.9.2"
package_resolver:
dependency: transitive
description:
@ -452,7 +452,7 @@ packages:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.7+13"
version: "0.9.7+14"
web_socket_channel:
dependency: transitive
description:

View File

@ -1,6 +1,6 @@
name: flutter_gherkin
description: A Gherkin / Cucumber parser and test runner for Dart and Flutter
version: 1.1.7+6
version: 1.1.7+7
homepage: https://github.com/jonsamwell/flutter_gherkin
environment:
@ -15,7 +15,7 @@ dependencies:
sdk: flutter
glob: ^1.1.7
meta: ">=1.1.6 <2.0.0"
gherkin: ^1.1.7
gherkin: 1.1.7
# gherkin:
# path: ../dart_gherkin