Flutter: How to implement FlutterError.OnError correctly
Asked Answered
A

3

15

Can someone show me how to implement overriding flutter errors during widget test so I can check for my own custom errors.

I have seen snippets online mentioning this but all of my implementations fail

void main() {

  testWidgets('throws an error when scanning unknown term types', (WidgetTester tester) async {
    await tester.pumpWidget(injectTestWidget(new ItemScanScreen()));

    await tester.enterText(find.byKey(new Key('term')), '');
    await tester.tap(find.byIcon(Icons.send));
    await tester.pump();

    expect(
      tester.takeException(),
      isInstanceOf<UnrecognizedTermException>(),
      reason: 'should have thrown an UnrecognizedTermException error but didn\'t',
    );
  });
}

the code above fails with the message below even though it looks like it in fact 'caught' my error:

The following UnrecognizedTermException was thrown running a test:
Instance of 'UnrecognizedTermException'
...

I read that you could do something like the snippet below but it did not see how/where to implement it:

final errorHandled = expectAsync0((){});

FlutterError.onError = (errorDetails) {
  // handle error
  errorHandled();
});
Afoul answered 21/1, 2019 at 17:2 Comment(0)
A
4

This is was design for a test. I switched to wrapping logic in try/catch then running expect() on the "error message text" present concept. ex:

try {
   throw new UnrecognizedTermException();
 } catch (e) {
   setState(() => _status = e.errMsg());
}

// then in my test
expect(find.text('Could not determine the type of item you scanned'), findsOneWidget);
Afoul answered 21/1, 2019 at 18:53 Comment(3)
So if I understrand correctly, you managed the exception in your app and stopped trying to catch it in your tests. Is that right ?Gamba
Yes that is correct. Im checking for the "behavior" or "artifacts" of my custom exception rather than the exception itselfAfoul
Ok. I'd be interrested to know if there's actually a solution to the problem you explained in your question, or if the only way to do this is through a workaround. I'm in a quite similar situation and don't want to loose the Exception by catching it.Gamba
B
16

I use the code below in production to log errors to a server.

main.dart:

import 'dart:async';
import 'package:flutter/material.dart';
import 'logging.dart';

void main() async {
  FlutterError.onError = (FlutterErrorDetails details) async {
    new ErrorLogger().logError(details);
  };
  runZoned<Future<void>>(() async {
    // Your App Here
    runApp(MainApp());
  }, onError: (error, stackTrace) {
    new ErrorLogger().log(error, stackTrace);
  });
}

logging.dart:

class ErrorLogger {

  void logError(FlutterErrorDetails details) async {
    //FlutterError.dumpErrorToConsole(details);
    _sendToServer(details.exceptionAsString(), details.stack.toString());
  }

  void log(Object data, StackTrace stackTrace) async {  
      // print(data);
      // print(stackTrace);
    _sendToServer(data.toString(), stackTrace.toString());
  }

  void _sendToServer(String a, String b) async {
    // Implementation here
  }
}
Babar answered 5/6, 2019 at 3:1 Comment(3)
The original question is more about widget testing and custom errors rather than logging errors from source code. As expect doesn't work and FlutterError.onError is recommendend with expectAsync0, the answer should be consistant with the way a custom error can be tested in widget unit tests.Gamba
Could you explain why is the runZoned needed here, please?Annals
RunZoned Works like a "try/catch". Here more details: #21532424Babar
A
4

This is was design for a test. I switched to wrapping logic in try/catch then running expect() on the "error message text" present concept. ex:

try {
   throw new UnrecognizedTermException();
 } catch (e) {
   setState(() => _status = e.errMsg());
}

// then in my test
expect(find.text('Could not determine the type of item you scanned'), findsOneWidget);
Afoul answered 21/1, 2019 at 18:53 Comment(3)
So if I understrand correctly, you managed the exception in your app and stopped trying to catch it in your tests. Is that right ?Gamba
Yes that is correct. Im checking for the "behavior" or "artifacts" of my custom exception rather than the exception itselfAfoul
Ok. I'd be interrested to know if there's actually a solution to the problem you explained in your question, or if the only way to do this is through a workaround. I'm in a quite similar situation and don't want to loose the Exception by catching it.Gamba
M
0

Below version works me, where we need to ignore NetworkImageLoadException during the patrol integration tests

Future<void> ignoreNetworkImageLoadException() async {
    final originalOnError = FlutterError.onError!;
    FlutterError.onError = (FlutterErrorDetails data) {
      final e = data.exception;
      if (e is NetworkImageLoadException) {
        debugPrint('Ignoring expected network error: $e');
        return;
      }
      originalOnError(data);
    };
  }

and as this returns Future, I needed

await appHelper.ignoreNetworkImageLoadException();

in my tests.

Mycosis answered 19/4 at 11:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.