felangel / mocktail

A mock library for Dart inspired by mockito
https://pub.dev/packages/mocktail
MIT License
588 stars 80 forks source link

mocktail_image_network: HTTP request failed, statusCode: 400 under certain conditions #236

Closed andrewpmoore closed 2 months ago

andrewpmoore commented 2 months ago

Describe the bug I'm new to mocktail, but came across the mocktail_image_network after getting statusCode: 400 as documented. The problem I have is that under certain circumstances I still get the error. It seems that if the Network.image is called on the widget load, it works ok, but if there's some form of status change to conditionally show the image, after triggering something else, then it failed.

To Reproduce I've created a small sample using a delay to change from an image not showing to showing

Here's the test class

class _Test extends StatefulWidget {
  const _Test({super.key});

  @override
  State<_Test> createState() => _TestState();
}

class _TestState extends State<_Test> {

  bool validated = false;

  @override
  void initState() {
    super.initState();
    Future.delayed(const Duration(seconds: 1)).then((value) => setState(() {
      validated = true;
    }));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: (validated) ? Image.network('http://www.flutter.dev/image1.png') : SizedBox(),
    );
  }
}

and here's the actual test

    testWidgets('Simple timed image test', (WidgetTester tester) async {

      await mockNetworkImages(() async => (await tester.pumpWidget(
          const MaterialApp(
              home: _Test()),

      await tester.pumpAndSettle(const Duration(seconds: 5));

    });

Expected behavior I would expect the test to wait 5 seconds and then succeed, but it fails with a statusCode: 400 error. If I change the test to (validated) ? Image.network('http://www.flutter.dev/image1.png') : Image.network('http://www.dummy.png') ie replacing the SizedBox with another Image.network when the page loads, then it works fine

Screenshots If applicable, add screenshots to help explain your problem.

Logs Here's the output

The following NetworkImageLoadException was thrown resolving an image codec:
HTTP request failed, statusCode: 400, http://www.flutter.dev/image1.png

When the exception was thrown, this was the stack:
#0      NetworkImage._loadAsync (package:flutter/src/painting/_network_image_io.dart:115:9)
<asynchronous suspension>
#1      MultiFrameImageStreamCompleter._handleCodecReady (package:flutter/src/painting/image_stream.dart:985:3)
<asynchronous suspension>

Image provider: NetworkImage("http://www.flutter.dev/image1.png", scale: 1.0)
Image key: NetworkImage("http://www.flutter.dev/image1.png", scale: 1.0)

Paste the output of running flutter doctor -v here. [√] Flutter (Channel stable, 3.19.5, on Microsoft Windows [Version 10.0.19045.4291], locale en-GB) • Flutter version 3.19.5 on channel stable at c:\flutter • Dart plugin can be installed from: https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 17.0.9+0--11185874)

[√] VS Code (version 1.87.1) • VS Code at C:\Users\AndrewMoore\AppData\Local\Programs\Microsoft VS Code • Flutter extension can be installed from: https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[√] Connected device (4 available) • Pixel 6 Pro (mobile) • 1A071FDEE006PY • android-arm64 • Android 14 (API 34) • Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.19045.4291] • Chrome (web) • chrome • web-javascript • Google Chrome 123.0.6312.106 • Edge (web) • edge • web-javascript • Microsoft Edge 123.0.2420.97

[√] Network resources • All expected network resources are available.

felangel commented 2 months ago

Hi @andrewpmoore 👋 Thanks for opening an issue!

This is happening because your pumpAndSettle call is happening outside of the mock image network zone. The way to restructure the test to make it pass is:

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail_image_network/mocktail_image_network.dart';

class _Test extends StatefulWidget {
  const _Test();

  @override
  State<_Test> createState() => _TestState();
}

class _TestState extends State<_Test> {
  bool validated = false;

  @override
  void initState() {
    super.initState();
    Future<void>.delayed(const Duration(seconds: 1)).then(
      (value) => setState(() => validated = true),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: validated
          ? Image.network('http://www.flutter.dev/image1.png')
          : const SizedBox(),
    );
  }
}

void main() {
  testWidgets('Simple timed image test', (tester) async {
    await mockNetworkImages(() async {
      await tester.pumpWidget(const MaterialApp(home: _Test()));
      await tester.pumpAndSettle(const Duration(seconds: 5));
    });
  });
}

Hope that helps, closing for now!