Repository: Betterment/alchemist

Issue:
Assets image displayed for CI snapshot but not for platform
### Is there an existing issue for this?

- [X] I have searched the existing issues.

### Version

0.2.1

### Description

When I'm generating golden files using an asset image it is not rendered while it is rendered correctly for its ci equivalent.

### Steps to reproduce

1. Create a `TestAssetBundle` to load your assets

```dart
import 'dart:convert';

import 'package:flutter/services.dart';

class TestAssetBundle extends CachingAssetBundle {
  @override
  Future<String> loadString(String key, {bool cache = true}) async {
    final data = await load(key);
    return utf8.decode(data.buffer.asUint8List());
  }

  @override
  Future<ByteData> load(String key) => rootBundle.load(key);
}
```

2. Write a `goldenTest` with an `Image.asset` widget (wrapped inside a `DefaultAssetBundle`)

```dart
import 'package:alchemist/alchemist.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import '../../utils/test_asset_bundle.dart';

void main() {
  setUpAll(TestWidgetsFlutterBinding.ensureInitialized);

  group('Image.asset: Golden Tests', () {
    goldenTest(
      'renders correctly',
      fileName: 'image_asset',
      widget: GoldenTestGroup(
        children: [
          GoldenTestScenario(
            name: 'XYZ Monster.jpg asset',
            child: DefaultAssetBundle(
              bundle: TestAssetBundle(),
              child: Image.asset('assets/type/XYZ Monster.jpg'),
            ),
          ),
        ],
      ),
    );
  });
}
```

3. Run the command `flutter test --update-goldens`

### Expected behavior

The golden file generated should display the asset image.

### Screenshots

## Asset Image

![XYZ Monster](https://user-images.githubusercontent.com/14369698/155419057-4ea0edcd-e82c-4c28-8fe9-d1f731f31ef4.jpg)

## CI Golden

![image_asset](https://user-images.githubusercontent.com/14369698/155419216-65bb14d7-2314-4656-a5c2-a41d9e558957.png)

## Platform Golden

![image_asset](https://user-images.githubusercontent.com/14369698/155419257-58ff2d55-ac9d-4087-b5c8-065b050f26a6.png)




### Additional context and comments

* `flutter doctor -v`:

```bash
[√] Flutter (Channel stable, 2.10.2, on Microsoft Windows [version 10.0.22000.527], locale fr-FR)
    • Flutter version 2.10.2 at C:\src\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 097d3313d8 (5 days ago), 2022-02-18 19:33:08 -0600
    • Engine revision a83ed0e5e3
    • Dart version 2.16.1
    • DevTools version 2.9.2

[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at C:\Users\Guillaume\AppData\Local\Android\sdk
    • Platform android-30, build-tools 30.0.3
    • Java binary at: D:\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[√] Chrome - develop for the web
    • Chrome at C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

[!] Visual Studio - develop for Windows (Visual Studio Build Tools 2017 15.9.35)
    • Visual Studio at C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools
    • Visual Studio Build Tools 2017 version 15.9.28307.1500
    • Windows 10 SDK version 10.0.17763.0
    X Visual Studio 2019 or later is required.
      Download at https://visualstudio.microsoft.com/downloads/.
      Please install the "Desktop development with C++" workload, including all of its default components

[√] Android Studio (version 4.1)
    • Android Studio at D:\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)

[√] Connected device (3 available)
    • Windows (desktop) • windows • windows-x64    • Microsoft Windows [version 10.0.22000.527]
    • Chrome (web)      • chrome  • web-javascript • Google Chrome 98.0.4758.102
    • Edge (web)        • edge    • web-javascript • Microsoft Edge 97.0.1072.76

[√] HTTP Host Availability
    • All required HTTP hosts are available

! Doctor found issues in 1 category.
```



Fix patch:
diff --git a/lib/src/pumps.dart b/lib/src/pumps.dart
index bacf50c..543c354 100644
--- a/lib/src/pumps.dart
+++ b/lib/src/pumps.dart
@@ -1,4 +1,5 @@
 import 'package:alchemist/alchemist.dart';
+import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
 
 /// A function that may perform pumping actions to prime a golden test.
@@ -30,3 +31,34 @@ final pumpOnce = pumpNTimes(1);
 ///
 /// See [PumpAction] for more details.
 Future<void> onlyPumpAndSettle(WidgetTester tester) => tester.pumpAndSettle();
+
+/// A custom pump action to ensure that the images for all [Image],
+/// [FadeInImage], and [DecoratedBox] widgets are loaded before the golden file
+/// is generated.
+///
+/// See [PumpAction] for more details.
+Future<void> precacheImages(WidgetTester tester) async {
+  await tester.runAsync(() async {
+    final images = <Future<void>>[];
+    for (final element in find.byType(Image).evaluate()) {
+      final widget = element.widget as Image;
+      final image = widget.image;
+      images.add(precacheImage(image, element));
+    }
+    for (final element in find.byType(FadeInImage).evaluate()) {
+      final widget = element.widget as FadeInImage;
+      final image = widget.image;
+      images.add(precacheImage(image, element));
+    }
+    for (final element in find.byType(DecoratedBox).evaluate()) {
+      final widget = element.widget as DecoratedBox;
+      final decoration = widget.decoration;
+      if (decoration is BoxDecoration && decoration.image != null) {
+        final image = decoration.image!.image;
+        images.add(precacheImage(image, element));
+      }
+    }
+    await Future.wait(images);
+  });
+  await tester.pumpAndSettle();
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index f210e4f..6515066 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -18,7 +18,8 @@ dependencies:
   meta: ^1.3.0
 
 dev_dependencies:
-  mocktail: ^0.1.3
+  mocktail: ^0.3.0
+  mocktail_image_network: ^0.3.1
   test: ^1.17.12
   very_good_analysis: 2.4.0
 


Test patch:
diff --git a/lib/src/golden_test.dart b/lib/src/golden_test.dart
index fb85fa0..8cd007b 100644
--- a/lib/src/golden_test.dart
+++ b/lib/src/golden_test.dart
@@ -70,10 +70,9 @@ Future<void> loadFonts() async {
 /// golden image under the `goldens` directory. This name should be unique, and
 /// may not contain an extension (such as `.png`).
 ///
-/// The provided [widget] describes the scenarios and layout of the widgets that
-/// are included in the test. A child must be provided. Alchemist provides two
-/// widgets to make creating a golden test scenario. See [GoldenTestGroup] and
-/// [GoldenTestScenario] for more details.
+/// The provided [builder] builds the widget under test.
+/// Usually, it creates multiple scenarios using [GoldenTestGroup]
+/// and [GoldenTestScenario].
 ///
 /// The [description] must be a unique description for the test.
 ///
@@ -103,9 +102,9 @@ Future<void> loadFonts() async {
 /// prime the widget tree before golden evaluation. By default, it is set to
 /// [onlyPumpAndSettle], which will pump the widget tree and wait for all
 /// scheduled frames to be completed, but can be overridden to customize the
-/// pump behavior. For example, a button tap can be simulated using
-/// `tester.tap(finder)`, after which the tester can be pumped and settled.
-/// See [pumpOnce], [pumpNTimes] and [onlyPumpAndSettle] for more details.
+/// pump behavior.
+/// See [pumpOnce], [pumpNTimes], [onlyPumpAndSettle], and [precacheImages] for
+/// more details.
 ///
 /// The [whilePerforming] interaction, if provided, will be called with the
 /// [WidgetTester] to perform a desired interaction during the golden test.
@@ -131,7 +130,7 @@ Future<void> goldenTest(
   BoxConstraints constraints = const BoxConstraints(),
   PumpAction pumpBeforeTest = onlyPumpAndSettle,
   Interaction? whilePerforming,
-  required Widget widget,
+  required ValueGetter<Widget> builder,
 }) async {
   if (skip) return;
 
@@ -163,7 +162,7 @@ Future<void> goldenTest(
           fileName,
           goldensConfig.environmentName,
         ),
-        widget: widget,
+        widget: builder(),
         forceUpdate: config.forceUpdateGoldenFiles,
         obscureText: goldensConfig.obscureText,
         renderShadows: goldensConfig.renderShadows,
diff --git a/lib/src/golden_test_adapter.dart b/lib/src/golden_test_adapter.dart
index 2219467..64208f5 100644
--- a/lib/src/golden_test_adapter.dart
+++ b/lib/src/golden_test_adapter.dart
@@ -2,6 +2,7 @@ import 'dart:async';
 import 'dart:ui' as ui;
 
 import 'package:alchemist/src/blocked_text_image.dart';
+import 'package:alchemist/src/pumps.dart';
 import 'package:alchemist/src/utilities.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/rendering.dart';
@@ -161,6 +162,7 @@ abstract class GoldenTestAdapter {
     required BoxConstraints constraints,
     required ThemeData theme,
     required Widget widget,
+    required PumpAction pumpBeforeTest,
   });
 
   /// Generates an image of the widget at the given [finder] with all text
@@ -220,6 +222,7 @@ class FlutterGoldenTestAdapter extends GoldenTestAdapter {
     required BoxConstraints constraints,
     required ThemeData theme,
     required Widget widget,
+    required PumpAction pumpBeforeTest,
   }) async {
     final initialSize = Size(
       constraints.hasBoundedWidth ? constraints.maxWidth : 2000,
@@ -261,6 +264,8 @@ class FlutterGoldenTestAdapter extends GoldenTestAdapter {
 
     final shouldTryResize = !constraints.isTight;
 
+    await pumpBeforeTest(tester);
+
     if (shouldTryResize) {
       final childSize = tester.getSize(find.byKey(childKey));
       final newSize = Size(
diff --git a/lib/src/golden_test_runner.dart b/lib/src/golden_test_runner.dart
index 1fbb349..7a8d79f 100644
--- a/lib/src/golden_test_runner.dart
+++ b/lib/src/golden_test_runner.dart
@@ -84,6 +84,7 @@ class FlutterGoldenTestRunner extends GoldenTestRunner {
         rootKey: rootKey,
         textScaleFactor: textScaleFactor,
         constraints: constraints,
+        pumpBeforeTest: pumpBeforeTest,
         theme: themeData.copyWith(
           textTheme: obscureText
               ? themeData.textTheme.apply(
@@ -94,8 +95,6 @@ class FlutterGoldenTestRunner extends GoldenTestRunner {
         widget: widget,
       );
 
-      await pumpBeforeTest(tester);
-
       AsyncCallback? cleanup;
       if (whilePerforming != null) {
         cleanup = await whilePerforming(tester);
diff --git a/test/helpers/fake_test_asset_bundle.dart b/test/helpers/fake_test_asset_bundle.dart
new file mode 100644
index 0000000..d9f9d1d
--- /dev/null
+++ b/test/helpers/fake_test_asset_bundle.dart
@@ -0,0 +1,22 @@
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:alchemist/src/utilities.dart';
+
+final redPixelImage = base64Decode(
+  'iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAApElEQVR42u3RAQ0AAAjD'
+  'MO5fNCCDkC5z0HTVrisFCBABASIgQAQEiIAAAQJEQIAICBABASIgQAREQIAICBABASIg'
+  'QAREQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQARE'
+  'QIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQAQECBAgAgJEQIAIyPcGFY7H'
+  'nV2aPXoAAAAASUVORK5CYII=',
+);
+
+class FakeTestAssetBundle extends TestAssetBundle {
+  @override
+  Future<ByteData> load(String key) async {
+    if (key.endsWith('png')) {
+      return ByteData.view(redPixelImage.buffer);
+    }
+    return super.load(key);
+  }
+}
diff --git a/test/helpers/helpers.dart b/test/helpers/helpers.dart
new file mode 100644
index 0000000..7ea7a9d
--- /dev/null
+++ b/test/helpers/helpers.dart
@@ -0,0 +1,1 @@
+export 'fake_test_asset_bundle.dart';
diff --git a/test/smoke_tests/asset_image_smoke_test.dart b/test/smoke_tests/asset_image_smoke_test.dart
new file mode 100644
index 0000000..e0b4646
--- /dev/null
+++ b/test/smoke_tests/asset_image_smoke_test.dart
@@ -0,0 +1,20 @@
+import 'package:alchemist/src/golden_test.dart';
+import 'package:alchemist/src/pumps.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../helpers/helpers.dart';
+
+void main() {
+  group('smoke test', () {
+    goldenTest(
+      'succeeds with an asset image',
+      fileName: 'asset_image_smoke_test',
+      pumpBeforeTest: precacheImages,
+      builder: () => DefaultAssetBundle(
+        bundle: FakeTestAssetBundle(),
+        child: Image.asset('test.png'),
+      ),
+    );
+  });
+}
diff --git a/test/smoke_tests/composited_transform_smoke_test.dart b/test/smoke_tests/composited_transform_smoke_test.dart
index bcce0bb..ff6e66c 100644
--- a/test/smoke_tests/composited_transform_smoke_test.dart
+++ b/test/smoke_tests/composited_transform_smoke_test.dart
@@ -31,7 +31,7 @@ void main() {
     goldenTest(
       'succeeds with CompositedTransformFollower',
       fileName: 'composited_transform_smoke_test',
-      widget: _SmokeTest(),
+      builder: _SmokeTest.new,
     );
   });
 }
diff --git a/test/smoke_tests/goldens/ci/asset_image_smoke_test.png b/test/smoke_tests/goldens/ci/asset_image_smoke_test.png
new file mode 100644
index 0000000..a417503
Binary files /dev/null and b/test/smoke_tests/goldens/ci/asset_image_smoke_test.png differ
diff --git a/test/smoke_tests/interactions_smoke_test.dart b/test/smoke_tests/interactions_smoke_test.dart
index 935a869..2467452 100644
--- a/test/smoke_tests/interactions_smoke_test.dart
+++ b/test/smoke_tests/interactions_smoke_test.dart
@@ -26,21 +26,21 @@ void main() {
     goldenTest(
       'succeeds in regular state',
       fileName: 'interactions_smoke_test_regular',
-      widget: buildSmokeTestGroup(),
+      builder: buildSmokeTestGroup,
     );
 
     goldenTest(
       'succeeds while pressed',
       fileName: 'interactions_smoke_test_pressed',
       whilePerforming: press(find.byType(ElevatedButton)),
-      widget: buildSmokeTestGroup(),
+      builder: buildSmokeTestGroup,
     );
 
     goldenTest(
       'succeeds while long pressed',
       fileName: 'interactions_smoke_test_long_pressed',
       whilePerforming: longPress(find.byType(ElevatedButton)),
-      widget: buildSmokeTestGroup(),
+      builder: buildSmokeTestGroup,
     );
   });
 }
diff --git a/test/smoke_tests/timer_button_smoke_test.dart b/test/smoke_tests/timer_button_smoke_test.dart
index 361fc1e..42f10ac 100644
--- a/test/smoke_tests/timer_button_smoke_test.dart
+++ b/test/smoke_tests/timer_button_smoke_test.dart
@@ -27,7 +27,7 @@ void main() {
         await tester.tap(find.byType(TimerButton));
         await tester.pumpAndSettle();
       },
-      widget: const TimerButton(),
+      builder: () => const TimerButton(),
     );
   });
 }
diff --git a/test/src/golden_test_adapter_test.dart b/test/src/golden_test_adapter_test.dart
index 93840c9..1f44d2c 100644
--- a/test/src/golden_test_adapter_test.dart
+++ b/test/src/golden_test_adapter_test.dart
@@ -4,6 +4,7 @@ import 'package:alchemist/src/blocked_text_image.dart';
 import 'package:alchemist/src/golden_test_adapter.dart';
 import 'package:alchemist/src/golden_test_group.dart';
 import 'package:alchemist/src/golden_test_scenario.dart';
+import 'package:alchemist/src/pumps.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:mocktail/mocktail.dart';
@@ -20,7 +21,7 @@ class MockRenderObject extends Mock implements RenderObject {
 
 void main() {
   setUpAll(() {
-    registerFallbackValue<RenderObject>(MockRenderObject());
+    registerFallbackValue(MockRenderObject());
   });
   group('overrides', () {
     group('goldenFileExpectationFn', () {
@@ -213,6 +214,7 @@ void main() {
             textScaleFactor: 1,
             constraints: const BoxConstraints(),
             theme: ThemeData.light(),
+            pumpBeforeTest: onlyPumpAndSettle,
             widget: buildGroup(),
           );
 
@@ -233,6 +235,7 @@ void main() {
             textScaleFactor: 1,
             constraints: BoxConstraints.tight(providedSize),
             theme: ThemeData.light(),
+            pumpBeforeTest: onlyPumpAndSettle,
             widget: buildGroup(),
           );
 
@@ -256,6 +259,7 @@ void main() {
             textScaleFactor: 1,
             constraints: const BoxConstraints(),
             theme: ThemeData.light(),
+            pumpBeforeTest: onlyPumpAndSettle,
             widget: buildGroup(),
           );
 
@@ -286,6 +290,7 @@ void main() {
               minHeight: minSize.height,
             ),
             theme: ThemeData.light(),
+            pumpBeforeTest: onlyPumpAndSettle,
             widget: buildGroup(),
           );
 
@@ -326,6 +331,7 @@ void main() {
               maxHeight: maxSize.height,
             ),
             theme: ThemeData.light(),
+            pumpBeforeTest: onlyPumpAndSettle,
             widget: buildGroup(),
           );
 
@@ -342,6 +348,7 @@ void main() {
           textScaleFactor: 2,
           constraints: const BoxConstraints(),
           theme: ThemeData.light(),
+          pumpBeforeTest: onlyPumpAndSettle,
           widget: buildGroup(),
         );
 
@@ -366,6 +373,7 @@ void main() {
             textScaleFactor: 1,
             constraints: const BoxConstraints(),
             theme: theme,
+            pumpBeforeTest: onlyPumpAndSettle,
             widget: buildGroup(),
           );
 
@@ -397,6 +405,20 @@ void main() {
           );
         },
       );
+
+      testWidgets('calls the provided pumpBeforeTest', (tester) async {
+        var pumpBeforeTestCalled = false;
+        await adapter.pumpGoldenTest(
+          tester: tester,
+          textScaleFactor: 2,
+          constraints: const BoxConstraints(),
+          theme: ThemeData.light(),
+          pumpBeforeTest: (_) async => pumpBeforeTestCalled = true,
+          widget: buildGroup(),
+        );
+
+        expect(pumpBeforeTestCalled, isTrue);
+      });
     });
   });
 }
diff --git a/test/src/golden_test_runner_test.dart b/test/src/golden_test_runner_test.dart
index fbe2530..3fd866d 100644
--- a/test/src/golden_test_runner_test.dart
+++ b/test/src/golden_test_runner_test.dart
@@ -15,11 +15,11 @@ class MockWidgetTester extends Mock implements WidgetTester {}
 
 void main() {
   setUpAll(() {
-    registerFallbackValue<WidgetTester>(MockWidgetTester());
-    registerFallbackValue<BoxConstraints>(const BoxConstraints());
-    registerFallbackValue<ThemeData>(ThemeData.light());
-    registerFallbackValue<Widget>(const SizedBox.square());
-    registerFallbackValue<Finder>(find.byType(Widget));
+    registerFallbackValue(MockWidgetTester());
+    registerFallbackValue(const BoxConstraints());
+    registerFallbackValue(ThemeData.light());
+    registerFallbackValue(const SizedBox.square());
+    registerFallbackValue(find.byType(Widget));
   });
 
   group('Overrides', () {
@@ -44,16 +44,11 @@ void main() {
   group('GoldenTestRunner', () {
     const goldenTestRunner = FlutterGoldenTestRunner();
     late MockAdapter adapter;
-    var pumpBeforeTestCalled = false;
     var cleanupCalled = false;
     var interactionCalled = false;
     var goldenFileExpectationCalled = false;
     var matcherInvocationCalled = false;
 
-    Future<void> pumpBeforeTest(WidgetTester tester) async {
-      pumpBeforeTestCalled = true;
-    }
-
     Future<void> cleanup() async {
       cleanupCalled = true;
     }
@@ -79,7 +74,6 @@ void main() {
       adapter = MockAdapter();
       goldenTestAdapter = adapter;
 
-      pumpBeforeTestCalled = false;
       cleanupCalled = false;
       interactionCalled = false;
       goldenFileExpectationCalled = false;
@@ -92,6 +86,7 @@ void main() {
           textScaleFactor: any(named: 'textScaleFactor'),
           constraints: any(named: 'constraints'),
           theme: any(named: 'theme'),
+          pumpBeforeTest: any(named: 'pumpBeforeTest'),
           widget: any(named: 'widget'),
         ),
       ).thenAnswer((_) async {});
@@ -108,7 +103,7 @@ void main() {
       ).thenReturn(goldenFileExpectation);
 
       when(
-        () => goldenTestAdapter.withForceUpdateGoldenFiles(
+        () => goldenTestAdapter.withForceUpdateGoldenFiles<void>(
           callback: any(named: 'callback'),
         ),
       ).thenAnswer((invocation) async {
@@ -116,7 +111,6 @@ void main() {
         await (invocation.namedArguments[#callback]
                 as MatchesGoldenFileInvocation<void>)
             .call();
-        return;
       });
     });
 
@@ -141,12 +135,10 @@ void main() {
         goldenPath: 'path/to/golden',
         widget: Container(),
         theme: theme,
-        pumpBeforeTest: pumpBeforeTest,
         whilePerforming: interaction,
         obscureText: true,
       );
 
-      expect(pumpBeforeTestCalled, isTrue);
       expect(interactionCalled, isTrue);
       expect(cleanupCalled, isTrue);
       expect(goldenFileExpectationCalled, isTrue);
@@ -159,6 +151,7 @@ void main() {
           textScaleFactor: any(named: 'textScaleFactor'),
           constraints: any(named: 'constraints'),
           theme: captureAny(named: 'theme'),
+          pumpBeforeTest: any(named: 'pumpBeforeTest'),
           widget: any(named: 'widget'),
         ),
       ).captured.first as ThemeData;
@@ -189,7 +182,6 @@ void main() {
         widget: Container(),
       );
 
-      expect(pumpBeforeTestCalled, isFalse);
       expect(interactionCalled, isFalse);
       expect(cleanupCalled, isFalse);
       expect(goldenFileExpectationCalled, isTrue);
@@ -202,6 +194,7 @@ void main() {
           textScaleFactor: any(named: 'textScaleFactor'),
           constraints: any(named: 'constraints'),
           theme: captureAny(named: 'theme'),
+          pumpBeforeTest: any(named: 'pumpBeforeTest'),
           widget: any(named: 'widget'),
         ),
       ).captured.first as ThemeData;
@@ -250,7 +243,7 @@ void main() {
           goldenPath: 'path/to/golden',
           renderShadows: true,
           widget: Container(),
-          pumpBeforeTest: (_) async {
+          whilePerforming: (_) {
             debugDisableShadowsDuringTestRun = debugDisableShadows;
             throw givenException;
           },
diff --git a/test/src/golden_test_test.dart b/test/src/golden_test_test.dart
index 7f959d4..86ef46c 100644
--- a/test/src/golden_test_test.dart
+++ b/test/src/golden_test_test.dart
@@ -39,6 +39,7 @@ class FakeGoldenTestAdapter extends Mock implements GoldenTestAdapter {
     required double textScaleFactor,
     required BoxConstraints constraints,
     required ThemeData theme,
+    required PumpAction pumpBeforeTest,
     required Widget widget,
   }) {
     return Future.value();
@@ -78,9 +79,9 @@ class FakeGoldenTestAdapter extends Mock implements GoldenTestAdapter {
 
 void main() {
   setUpAll(() {
-    registerFallbackValue<WidgetTester>(MockWidgetTester());
-    registerFallbackValue<Widget>(Container());
-    registerFallbackValue<BoxConstraints>(const BoxConstraints());
+    registerFallbackValue(MockWidgetTester());
+    registerFallbackValue(Container());
+    registerFallbackValue(const BoxConstraints());
   });
 
   group('goldenTest', () {
@@ -120,7 +121,7 @@ void main() {
         goldenTest(
           'golden test test',
           fileName: 'test.png',
-          widget: Container(),
+          builder: () => const SizedBox(),
         ),
         throwsAssertionError,
       );
@@ -151,7 +152,7 @@ void main() {
         run: () async => goldenTest(
           'test golden test',
           fileName: 'test_golden_test',
-          widget: widget,
+          builder: () => widget,
         ),
       );
       expect(filePathResolverCalled, isTrue);
diff --git a/test/src/pumps_test.dart b/test/src/pumps_test.dart
index 62ac3b0..4df8989 100644
--- a/test/src/pumps_test.dart
+++ b/test/src/pumps_test.dart
@@ -1,9 +1,41 @@
 import 'package:alchemist/alchemist.dart';
+import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:mocktail/mocktail.dart';
+import 'package:mocktail_image_network/mocktail_image_network.dart';
+
+import '../helpers/helpers.dart';
 
 class MockWidgetTester extends Mock implements WidgetTester {}
 
+Future<bool> _isCached(ImageProvider<Object> image, Finder findImage) async {
+  final cacheStatus = await image.obtainCacheStatus(
+    configuration: createLocalImageConfiguration(
+      findImage.evaluate().first,
+    ),
+  );
+  return cacheStatus?.keepAlive ?? false;
+}
+
+extension on CommonFinders {
+  Finder fadeInImage(ImageProvider<Object> image) => byWidgetPredicate(
+        (widget) => widget is FadeInImage && widget.image == image,
+      );
+
+  Finder decorationImage(ImageProvider<Object> image) => byWidgetPredicate(
+        (widget) {
+          if (widget is DecoratedBox) {
+            final decoration = widget.decoration;
+            if (decoration is BoxDecoration &&
+                decoration.image?.image == image) {
+              return true;
+            }
+          }
+          return false;
+        },
+      );
+}
+
 void main() {
   group('Custom pump functions', () {
     late WidgetTester tester;
@@ -52,5 +84,140 @@ void main() {
         verify(() => tester.pumpAndSettle()).called(1);
       });
     });
+
+    group('precacheImages', () {
+      const networkImage = NetworkImage('https://fakeurl.com/image.png');
+      const assetImage = AssetImage('path.png');
+      final memoryImage = MemoryImage(redPixelImage);
+
+      testWidgets(
+        'caches all Image widgets',
+        (tester) => mockNetworkImages(() async {
+          await tester.pumpWidget(
+            DefaultAssetBundle(
+              bundle: FakeTestAssetBundle(),
+              child: Column(
+                children: [
+                  const Image(
+                    image: networkImage,
+                  ),
+                  const Image(
+                    image: assetImage,
+                  ),
+                  Image(
+                    image: memoryImage,
+                  ),
+                ],
+              ),
+            ),
+          );
+          await precacheImages(tester);
+
+          await expectLater(
+            _isCached(networkImage, find.image(networkImage)),
+            completion(isTrue),
+          );
+          await expectLater(
+            _isCached(assetImage, find.image(assetImage)),
+            completion(isTrue),
+          );
+          await expectLater(
+            _isCached(memoryImage, find.image(memoryImage)),
+            completion(isTrue),
+          );
+        }),
+      );
+
+      testWidgets(
+        'caches all FadeInImage widgets',
+        (tester) => mockNetworkImages(() async {
+          await tester.pumpWidget(
+            DefaultAssetBundle(
+              bundle: FakeTestAssetBundle(),
+              child: Column(
+                children: [
+                  FadeInImage(
+                    image: networkImage,
+                    placeholder: MemoryImage(redPixelImage),
+                  ),
+                  FadeInImage(
+                    image: assetImage,
+                    placeholder: MemoryImage(redPixelImage),
+                  ),
+                  FadeInImage(
+                    image: memoryImage,
+                    placeholder: MemoryImage(redPixelImage),
+                  ),
+                ],
+              ),
+            ),
+          );
+          await precacheImages(tester);
+
+          await expectLater(
+            _isCached(networkImage, find.fadeInImage(networkImage)),
+            completion(isTrue),
+          );
+          await expectLater(
+            _isCached(assetImage, find.fadeInImage(assetImage)),
+            completion(isTrue),
+          );
+          await expectLater(
+            _isCached(memoryImage, find.fadeInImage(memoryImage)),
+            completion(isTrue),
+          );
+        }),
+      );
+
+      testWidgets(
+        'caches all DecoratedBox widgets',
+        (tester) => mockNetworkImages(() async {
+          await tester.pumpWidget(
+            DefaultAssetBundle(
+              bundle: FakeTestAssetBundle(),
+              child: Column(
+                children: [
+                  const DecoratedBox(
+                    decoration: BoxDecoration(
+                      image: DecorationImage(
+                        image: networkImage,
+                      ),
+                    ),
+                  ),
+                  const DecoratedBox(
+                    decoration: BoxDecoration(
+                      image: DecorationImage(
+                        image: assetImage,
+                      ),
+                    ),
+                  ),
+                  DecoratedBox(
+                    decoration: BoxDecoration(
+                      image: DecorationImage(
+                        image: memoryImage,
+                      ),
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          );
+          await precacheImages(tester);
+
+          await expectLater(
+            _isCached(networkImage, find.decorationImage(networkImage)),
+            completion(isTrue),
+          );
+          await expectLater(
+            _isCached(assetImage, find.decorationImage(assetImage)),
+            completion(isTrue),
+          );
+          await expectLater(
+            _isCached(memoryImage, find.decorationImage(memoryImage)),
+            completion(isTrue),
+          );
+        }),
+      );
+    });
   });
 }