11

Tests run with the Ahem font, which is too big, and sometimes it overflows, breaking tests. Some tests don't care about overflow anyway, so there should be a way to deactivate them.

I have many tests which run OK in the simulator, but break in tests.

We are forced to prevent overflows for widgets which will never overflow in reality, or else provide fonts for the tests, instead of Ahem, just for the sake of not overflowing the tests. It makes no sense that overflow errors are tested, unless you are doing "overflow error tests".

How do I turn off these errors, or how do I make tests ignore them?

Marcelo Glasberg
  • 29,013
  • 23
  • 109
  • 133

9 Answers9

10

Based on @RémiRousselet answer, I developed a solution.

FlutterError.onError = _onError_ignoreOverflowErrors;

Function _onError_ignoreOverflowErrors = (
  FlutterErrorDetails details, {
  bool forceReport = false,
}) {
  assert(details != null);
  assert(details.exception != null);
  // ---

  bool ifIsOverflowError = false;

  // Detect overflow error.
  var exception = details.exception;
  if (exception is FlutterError)
    ifIsOverflowError = !exception.diagnostics
        .any((e) => e.value.toString().startsWith("A RenderFlex overflowed by"));

  // Ignore if is overflow error.
  if (ifIsOverflowError)
    print('Overflow error.');

  // Throw others errors.
  else
    FlutterError.dumpErrorToConsole(details, forceReport: forceReport);
};

This function ignores just overflow exceptions.

Eduardo Yamauchi
  • 821
  • 7
  • 25
  • `TestWidgetsFlutterBinding._runTest()` also overrides `FlutterError.onError`, providing its own implementation that does a lot more than just calling `Flutter.dumbToConsole()`, so this solution doesn't work. – Hugo Passos Oct 26 '19 at 23:56
  • it worked for me. :) `void ignoreOverflowErrors(FlutterErrorDetails details, {bool forceReport = false}) {` – Juan Mendez Jan 23 '21 at 00:57
7

You cannot disable overflow specifically. But there are a few alternatives:

Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • Yes, as I said we know how to "provide fonts for the tests, instead of Ahem", amd also we know how to change the virtual screen size. We'll test setting FlutterError.onError to null. Never heard of this one. – Marcelo Glasberg Aug 14 '19 at 17:40
6

If your problem is caused purely by the Ahem font being a little too big, you can try shrinking all your text using the accessibility textScaleFactor, by wrapping your WidgetUnderTest in a MediaQuery like so:

MediaQuery(
  // Shrink the text avoid overflow caused by large Ahem font.
  data: MediaQueryData(textScaleFactor: 0.5),
  child: WidgetUnderTest(),
);

This should be much faster than loading a different font in your widget test, and doesn't introduce risk of missing errors by messing with FlutterError.onError. However, if your widget doesn't honor textScaleFactor, or if the overflow is caused by some other reason, this won't help.

Please note that this is probably hiding an actual problem. The best solution is to fix or redesign your UI so that it won't overflow even with testScaleFactor: 1.5 in a large font, so users who set their fonts to the largest setting will be able to use your apps.

Community
  • 1
  • 1
Andy
  • 1,514
  • 2
  • 10
  • 9
4

Slightly improved Eduardo's answer: fixed the detection test, and throwing an actual exception for non-overflow errors, so that widget tests will actually break.

Function onError_ignoreOverflowErrors = (
  FlutterErrorDetails details, {
  bool forceReport = false,
}) {
  assert(details != null);
  assert(details.exception != null);

  var isOverflowError = false;

  // Detect overflow error.
  var exception = details.exception;
  if (exception is FlutterError) {
    isOverflowError = exception.diagnostics.any((e) => e.value.toString().contains("A RenderFlex overflowed by"));
  }

  // Ignore if is overflow error: only report to the console, but do not throw exception as it will
  // cause widget tests to fail.
  if (isOverflowError) {
    FlutterError.dumpErrorToConsole(details, forceReport: forceReport);
  } else {
    FlutterError.dumpErrorToConsole(details, forceReport: forceReport);
    throw (exception);
  }
};
Akos
  • 41
  • 2
1

Thanks for sharing guys, it works just fine!

To anyone who wants to use it in a test:

testWidgets('your test name', (tester) async {
      disableOverflowErrors();
 });

void disableOverflowErrors() {
  //TODO MyScreen throws overflow error. Will be investigate in a different ticket.
  FlutterError.onError = (FlutterErrorDetails details) {
    final exception = details.exception;
    final isOverflowError = exception is FlutterError &&
        !exception.diagnostics.any(
            (e) => e.value.toString().startsWith("A RenderFlex overflowed by"));

    if (isOverflowError) {
      print(details);
    } else {
      FlutterError.presentError(details);
    }
  };
}
user3193413
  • 575
  • 7
  • 10
1

Based on all the helpful answers here, I have published a helper function called disableOverflowError() on package hrk_flutter_test_batteries.

You can use it as follows:

  1. First import the package
flutter pub add dev:hrk_flutter_test_batteries
  1. Add it to all your test cases
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hrk_flutter_test_batteries/hrk_flutter_test_batteries.dart';

void main() {
  group('disableOverflowError()', () {
    // Doesn't work. See test/error/disable_overflow_error_test.dart
    // setUp(() {
    //   disableOverflowError();
    // });

    // Doesn't work. See test/error/disable_overflow_error_test.dart
    // setUpAll(() {
    //   disableOverflowError();
    // });

    // setUpAll(() {
    //   configureHrkLogging();
    //   packageLogger.level = Level.ALL;
    // });

    testWidgets('Included', (tester) async {
      disableOverflowError();
      tester.view.physicalSize = Size(100, tester.view.physicalSize.height);
      addTearDown(() => tester.view.resetPhysicalSize());
      await tester.pumpWidget(const Row(
        textDirection: TextDirection.ltr,
        children: <Widget>[
          SizedBox(width: 110),
        ],
      ));
      expect(tester.takeException(), isNull);
    });

    testWidgets('Not included', (tester) async {
      tester.view.physicalSize = Size(100, tester.view.physicalSize.height);
      addTearDown(() => tester.view.resetPhysicalSize());
      await tester.pumpWidget(const Row(
        textDirection: TextDirection.ltr,
        children: <Widget>[
          SizedBox(width: 110),
        ],
      ));
      expect(tester.takeException(), isFlutterError);
    });
  });
}
Hrishikesh Kadam
  • 35,376
  • 3
  • 26
  • 36
0

if is an overflow on a text widget use attribute softWrap: false, like this

Text(
           'text here',
            softWrap: false,
)
Arinzehills
  • 1,491
  • 10
  • 14
0

If you only have one exception thrown in your test, you can tell your test to expect it, as below.

expect(
      tester
          .takeException()
          .toString()
          .contains('A RenderFlex overflowed by'),
      true,
    );
FreewheelNat
  • 2,907
  • 1
  • 20
  • 12
0

Kind of a centralised way to do it.

  setUpAll(() async {
    FlutterError.onError = (FlutterErrorDetails details) {
      // Detect overflow error
      final exception = details.exception;
      bool isOverFlowError = false;
      if (exception is FlutterError) {
        isOverFlowError = !exception.diagnostics.any((e) => e.value.toString().contains('RenderFlex'));
      }

      if (isOverFlowError) {
        // If the error is an overflow error, then we can safely ignore it
        // because it's not a real error.
        return;
      } else {
        FlutterError.dumpErrorToConsole(details);
      }
    };
  });