Skip to content

Commit 1aa6634

Browse files
Nurhan TurgutEgor
Nurhan Turgut
authored and
Egor
committed
[e2e] Adding failure details for driver tests (flutter#2593)
* Adding failure details for driver tests * formate fix * addressing reviewer comments * addressing the reviewer comments. part 2 * fix error in response_data * addressing reviewer comments * fixing formatting * addressin reviewer comments. Carrying the repeated driver test code to e2e package * chaning the failure's details type from map to list * format changes * add documentation to public members * formatting * changing namespace name
1 parent cabaf94 commit 1aa6634

File tree

8 files changed

+170
-25
lines changed

8 files changed

+170
-25
lines changed

packages/e2e/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.4.0
2+
3+
* **Breaking change** Driver request_data call's response has changed to
4+
encapsulate the failure details.
5+
* Details for failure cases are added: failed method name, stack trace.
6+
17
## 0.3.0+1
28

39
* Replace deprecated `getFlutterEngine` call on Android.

packages/e2e/README.md

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,11 @@ Put the a file named `<package_name>_e2e_test.dart` in the app' `test_driver` di
4343

4444
```dart
4545
import 'dart:async';
46-
import 'dart:io';
47-
import 'package:flutter_driver/flutter_driver.dart';
48-
49-
Future<void> main() async {
50-
final FlutterDriver driver = await FlutterDriver.connect();
51-
final String result =
52-
await driver.requestData(null, timeout: const Duration(minutes: 1));
53-
await driver.close();
54-
exit(result == 'pass' ? 0 : 1);
55-
}
46+
47+
import 'package:e2e/e2e_driver.dart' as e2e;
48+
49+
Future<void> main() async => e2e.main();
50+
5651
```
5752

5853
To run a example app test with Flutter driver:
@@ -69,7 +64,7 @@ cd example
6964
flutter drive --driver=test_driver/<package_name>_test.dart test/<package_name>_e2e.dart
7065
```
7166

72-
You can run tests on web on release mode.
67+
You can run tests on web in release or profile mode.
7368

7469
First you need to make sure you have downloaded the driver for the browser.
7570

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
import 'dart:async';
2-
import 'dart:io';
32

4-
import 'package:flutter_driver/flutter_driver.dart';
3+
import 'package:e2e/e2e_driver.dart' as e2e;
54

6-
Future<void> main() async {
7-
final FlutterDriver driver = await FlutterDriver.connect();
8-
final String result =
9-
await driver.requestData(null, timeout: const Duration(minutes: 1));
10-
await driver.close();
11-
exit(result == 'pass' ? 0 : 1);
12-
}
5+
Future<void> main() async => e2e.main();
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import 'dart:async';
22

3+
import 'package:e2e/common.dart' as common;
34
import 'package:flutter_driver/flutter_driver.dart';
45
import 'package:test/test.dart';
56

67
Future<void> main() async {
78
test('fails gracefully', () async {
89
final FlutterDriver driver = await FlutterDriver.connect();
9-
final String result =
10+
final String jsonResult =
1011
await driver.requestData(null, timeout: const Duration(minutes: 1));
12+
common.Response response = common.Response.fromJson(jsonResult);
1113
await driver.close();
1214
expect(
13-
result,
14-
'fail',
15+
response.allTestsPassed,
16+
false,
1517
);
1618
});
1719
}

packages/e2e/lib/common.dart

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright 2019 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:convert';
6+
7+
/// An object sent from e2e back to the Flutter Driver in response to
8+
/// `request_data` command.
9+
class Response {
10+
final List<Failure> _failureDetails;
11+
12+
final bool _allTestsPassed;
13+
14+
/// Constructor to use for positive response.
15+
Response.allTestsPassed()
16+
: this._allTestsPassed = true,
17+
this._failureDetails = null;
18+
19+
/// Constructor for failure response.
20+
Response.someTestsFailed(this._failureDetails) : this._allTestsPassed = false;
21+
22+
/// Whether the test ran successfully or not.
23+
bool get allTestsPassed => _allTestsPassed;
24+
25+
/// If the result are failures get the formatted details.
26+
String get formattedFailureDetails =>
27+
_allTestsPassed ? '' : formatFailures(_failureDetails);
28+
29+
/// Failure details as a list.
30+
List<Failure> get failureDetails => _failureDetails;
31+
32+
/// Serializes this message to a JSON map.
33+
String toJson() => json.encode(<String, dynamic>{
34+
'result': allTestsPassed.toString(),
35+
'failureDetails': _failureDetailsAsString(),
36+
});
37+
38+
/// Deserializes the result from JSON.
39+
static Response fromJson(String source) {
40+
Map<String, dynamic> responseJson = json.decode(source);
41+
if (responseJson['result'] == 'true') {
42+
return Response.allTestsPassed();
43+
} else {
44+
return Response.someTestsFailed(
45+
_failureDetailsFromJson(responseJson['failureDetails']));
46+
}
47+
}
48+
49+
/// Method for formating the test failures' details.
50+
String formatFailures(List<Failure> failureDetails) {
51+
if (failureDetails.isEmpty) {
52+
return '';
53+
}
54+
55+
StringBuffer sb = StringBuffer();
56+
int failureCount = 1;
57+
failureDetails.forEach((Failure f) {
58+
sb.writeln('Failure in method: ${f.methodName}');
59+
sb.writeln('${f.details}');
60+
sb.writeln('end of failure ${failureCount.toString()}\n\n');
61+
failureCount++;
62+
});
63+
return sb.toString();
64+
}
65+
66+
/// Create a list of Strings from [_failureDetails].
67+
List<String> _failureDetailsAsString() {
68+
final List<String> list = List<String>();
69+
if (_failureDetails == null || _failureDetails.isEmpty) {
70+
return list;
71+
}
72+
73+
_failureDetails.forEach((Failure f) {
74+
list.add(f.toString());
75+
});
76+
77+
return list;
78+
}
79+
80+
/// Creates a [Failure] list using a json response.
81+
static List<Failure> _failureDetailsFromJson(List<dynamic> list) {
82+
final List<Failure> failureList = List<Failure>();
83+
list.forEach((s) {
84+
final String failure = s as String;
85+
failureList.add(Failure.fromJsonString(failure));
86+
});
87+
return failureList;
88+
}
89+
}
90+
91+
/// Representing a failure includes the method name and the failure details.
92+
class Failure {
93+
/// The name of the test method which failed.
94+
final String methodName;
95+
96+
/// The details of the failure such as stack trace.
97+
final String details;
98+
99+
/// Constructor requiring all fields during initialization.
100+
Failure(this.methodName, this.details);
101+
102+
/// Serializes the object to JSON.
103+
@override
104+
String toString() {
105+
return json.encode(<String, String>{
106+
'methodName': methodName,
107+
'details': details,
108+
});
109+
}
110+
111+
/// Decode a JSON string to create a Failure object.
112+
static Failure fromJsonString(String jsonString) {
113+
Map<String, dynamic> failure = json.decode(jsonString);
114+
return Failure(failure['methodName'], failure['details']);
115+
}
116+
}

packages/e2e/lib/e2e.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
67
import 'package:flutter_test/flutter_test.dart';
78
import 'package:flutter/foundation.dart';
89
import 'package:flutter/services.dart';
910
import 'package:flutter/widgets.dart';
1011

12+
import 'common.dart';
1113
import '_extension_io.dart' if (dart.library.html) '_extension_web.dart';
1214

1315
/// A subclass of [LiveTestWidgetsFlutterBinding] that reports tests results
@@ -36,6 +38,11 @@ class E2EWidgetsFlutterBinding extends LiveTestWidgetsFlutterBinding {
3638

3739
final Completer<bool> _allTestsPassed = Completer<bool>();
3840

41+
/// Stores failure details.
42+
///
43+
/// Failed test method's names used as key.
44+
final List<Failure> _failureMethodsDetails = List<Failure>();
45+
3946
/// Similar to [WidgetsFlutterBinding.ensureInitialized].
4047
///
4148
/// Returns an instance of the [E2EWidgetsFlutterBinding], creating and
@@ -63,7 +70,9 @@ class E2EWidgetsFlutterBinding extends LiveTestWidgetsFlutterBinding {
6370
case 'request_data':
6471
final bool allTestsPassed = await _allTestsPassed.future;
6572
response = <String, String>{
66-
'message': allTestsPassed ? 'pass' : 'fail',
73+
'message': allTestsPassed
74+
? Response.allTestsPassed().toJson()
75+
: Response.someTestsFailed(_failureMethodsDetails).toJson(),
6776
};
6877
break;
6978
case 'get_health':
@@ -94,6 +103,7 @@ class E2EWidgetsFlutterBinding extends LiveTestWidgetsFlutterBinding {
94103
reportTestException =
95104
(FlutterErrorDetails details, String testDescription) {
96105
_results[description] = 'failed';
106+
_failureMethodsDetails.add(Failure(testDescription, details.toString()));
97107
if (!_allTestsPassed.isCompleted) _allTestsPassed.complete(false);
98108
valueBeforeTest(details, testDescription);
99109
};

packages/e2e/lib/e2e_driver.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import 'dart:async';
2+
import 'dart:io';
3+
4+
import 'package:e2e/common.dart' as e2e;
5+
import 'package:flutter_driver/flutter_driver.dart';
6+
7+
Future<void> main() async {
8+
final FlutterDriver driver = await FlutterDriver.connect();
9+
final String jsonResult =
10+
await driver.requestData(null, timeout: const Duration(minutes: 1));
11+
final e2e.Response response = e2e.Response.fromJson(jsonResult);
12+
await driver.close();
13+
14+
if (response.allTestsPassed) {
15+
print('All tests passed.');
16+
exit(0);
17+
} else {
18+
print('Failure Details:\n${response.formattedFailureDetails}');
19+
exit(1);
20+
}
21+
}

packages/e2e/pubspec.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: e2e
22
description: Runs tests that use the flutter_test API as integration tests.
3-
version: 0.3.0+1
3+
version: 0.4.0
44
homepage: https://github.com/flutter/plugins/tree/master/packages/e2e
55

66
environment:
@@ -10,6 +10,8 @@ environment:
1010
dependencies:
1111
flutter:
1212
sdk: flutter
13+
flutter_driver:
14+
sdk: flutter
1315
flutter_test:
1416
sdk: flutter
1517

0 commit comments

Comments
 (0)