Skip to content

Commit b4c9e9e

Browse files
committed
tests: Add test cases for testing preserving stacktrace in Computed and Observer
1 parent dd7c42a commit b4c9e9e

File tree

2 files changed

+123
-47
lines changed

2 files changed

+123
-47
lines changed

flutter_mobx/test/flutter_mobx_test.dart

+106-47
Original file line numberDiff line numberDiff line change
@@ -59,45 +59,49 @@ void main() {
5959
expect(renderCount, equals(1));
6060
});
6161

62-
testWidgets("Observer.withBuiltChild's child doesn't re-render", (tester) async {
62+
testWidgets("Observer.withBuiltChild's child doesn't re-render",
63+
(tester) async {
6364
final message = Observable('Click');
64-
final key1 = UniqueKey();
65-
final key2 = UniqueKey();
66-
final key3 = UniqueKey();
67-
68-
await tester.pumpWidget(
69-
MaterialApp(
70-
home: Observer.withBuiltChild(
71-
builderWithChild: (context, child) {
72-
return Column(
73-
children: [
74-
ElevatedButton(onPressed: () => message.value = 'Clicked', child: Container()),
75-
Text(message.value, key: key1),
76-
child!,
77-
Builder(
78-
builder: (context) {
79-
return Text(message.value, key: key3);
80-
}
81-
),
82-
],
83-
);
84-
},
85-
child: Text(message.value, key: key2),
86-
),
87-
),
88-
);
89-
90-
expect(tester.widget<Text>(find.byKey(key1)).data, equals('Click'));
91-
expect(tester.widget<Text>(find.byKey(key2)).data, equals('Click'));
92-
expect(tester.widget<Text>(find.byKey(key3)).data, equals('Click'));
93-
94-
await tester.tap(find.byType(ElevatedButton));
95-
expect(message.value, equals('Clicked'));
96-
97-
await tester.pump();
98-
expect(tester.widget<Text>(find.byKey(key1)).data, equals('Clicked')); // Observer rebuilt the Text1
99-
expect(tester.widget<Text>(find.byKey(key2)).data, equals('Click')); // child Text2 did not change
100-
expect(tester.widget<Text>(find.byKey(key3)).data, equals('Clicked')); // Builder does not preserve from rebuild
65+
final key1 = UniqueKey();
66+
final key2 = UniqueKey();
67+
final key3 = UniqueKey();
68+
69+
await tester.pumpWidget(
70+
MaterialApp(
71+
home: Observer.withBuiltChild(
72+
builderWithChild: (context, child) {
73+
return Column(
74+
children: [
75+
ElevatedButton(
76+
onPressed: () => message.value = 'Clicked',
77+
child: Container()),
78+
Text(message.value, key: key1),
79+
child!,
80+
Builder(builder: (context) {
81+
return Text(message.value, key: key3);
82+
}),
83+
],
84+
);
85+
},
86+
child: Text(message.value, key: key2),
87+
),
88+
),
89+
);
90+
91+
expect(tester.widget<Text>(find.byKey(key1)).data, equals('Click'));
92+
expect(tester.widget<Text>(find.byKey(key2)).data, equals('Click'));
93+
expect(tester.widget<Text>(find.byKey(key3)).data, equals('Click'));
94+
95+
await tester.tap(find.byType(ElevatedButton));
96+
expect(message.value, equals('Clicked'));
97+
98+
await tester.pump();
99+
expect(tester.widget<Text>(find.byKey(key1)).data,
100+
equals('Clicked')); // Observer rebuilt the Text1
101+
expect(tester.widget<Text>(find.byKey(key2)).data,
102+
equals('Click')); // child Text2 did not change
103+
expect(tester.widget<Text>(find.byKey(key3)).data,
104+
equals('Clicked')); // Builder does not preserve from rebuild
101105
});
102106

103107
testWidgets('Observer build should call reaction.track', (tester) async {
@@ -172,6 +176,34 @@ void main() {
172176
expect(exception, isInstanceOf<MobXCaughtException>());
173177
});
174178

179+
testWidgets(
180+
'Observer should print full stacktrace of error coming from computed',
181+
(tester) async {
182+
late StackTrace stackTrace;
183+
final errorWrapper = await _testThrowingObserverWithStackTrace(
184+
tester,
185+
firstError: true,
186+
actionThrows: (tester) async {
187+
await tester.pumpWidget(
188+
Observer(
189+
builder: (context) {
190+
Computed(() {
191+
try {
192+
throw Exception();
193+
} on Exception catch (e, st) {
194+
stackTrace = st;
195+
rethrow;
196+
}
197+
}).value;
198+
},
199+
),
200+
);
201+
},
202+
);
203+
expect(errorWrapper.stackTrace, stackTrace);
204+
},
205+
);
206+
175207
testWidgets('Observer unmount should dispose Reaction', (tester) async {
176208
final mock = MockReaction();
177209
when(() => mock.hasObservables).thenReturn(true);
@@ -341,18 +373,45 @@ Future<MobXCaughtException> _testThrowingObserver(
341373
WidgetTester tester,
342374
Object errorToThrow,
343375
) async {
344-
late Object exception;
376+
return (await _testThrowingObserverWithStackTrace(
377+
tester,
378+
actionThrows: (tester) async {
379+
final count = Observable(0);
380+
await tester.pumpWidget(FlutterErrorThrowingObserver(
381+
errorToThrow: errorToThrow,
382+
builder: (context) => Text(count.value.toString()),
383+
));
384+
count.value++;
385+
},
386+
))
387+
.exception as MobXCaughtException;
388+
}
389+
390+
class _ErrorWrapper {
391+
final Object exception;
392+
final StackTrace? stackTrace;
393+
394+
_ErrorWrapper({required this.exception, required this.stackTrace});
395+
}
396+
397+
Future<_ErrorWrapper> _testThrowingObserverWithStackTrace(
398+
WidgetTester tester, {
399+
required Future<void> Function(WidgetTester tester) actionThrows,
400+
bool firstError = false,
401+
}) async {
402+
_ErrorWrapper? errorWrapper;
345403
final prevOnError = FlutterError.onError;
346-
FlutterError.onError = (details) => exception = details.exception;
404+
FlutterError.onError = (details) {
405+
if (firstError && errorWrapper != null) {
406+
return;
407+
}
408+
errorWrapper =
409+
_ErrorWrapper(exception: details.exception, stackTrace: details.stack);
410+
};
347411

348412
try {
349-
final count = Observable(0);
350-
await tester.pumpWidget(FlutterErrorThrowingObserver(
351-
errorToThrow: errorToThrow,
352-
builder: (context) => Text(count.value.toString()),
353-
));
354-
count.value++;
355-
return exception as MobXCaughtException;
413+
await actionThrows(tester);
414+
return errorWrapper!;
356415
} finally {
357416
FlutterError.onError = prevOnError;
358417
}

mobx/test/exceptions_test.dart

+17
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,21 @@ void main() {
2626
expect(ex.stackTrace, isNotNull);
2727
}
2828
});
29+
30+
test('should preserve stacktrace', () async {
31+
late StackTrace stackTrace;
32+
try {
33+
Computed(() {
34+
try {
35+
throw Exception();
36+
} on Exception catch (e, st) {
37+
stackTrace = st;
38+
rethrow;
39+
}
40+
}).value;
41+
} on MobXCaughtException catch (e, st) {
42+
expect(st, stackTrace);
43+
expect(st, e.stackTrace);
44+
}
45+
});
2946
}

0 commit comments

Comments
 (0)