🌊

FlutterでCachedNetworkImageのVRTを実施

に公開

Flutterでcached_network_imageを使ったゴールデンテスト(VRT)実装ガイド

はじめに

cached_network_imageは、Flutterで画像をキャッシュ付きでネットワークから読み込む便利なパッケージです。
しかしゴールデンテスト(Visual Regression Testing, VRT)では、ネットワーク依存の状態遷移(ローディング → 正常表示)をそのまま扱うと、再現性の低いテストになってしまいます。

そこで本記事では、CacheManagerを差し替えてローカルPNGファイルを参照する形に変更する方法を解説します。


なぜCacheManagerを差し替えるのか?

  • CachedNetworkImageは内部でCacheManagerを使用してネットワークから画像を取得・キャッシュしています。
  • テストではネットワークアクセスを避け、毎回同じ画像を返す必要があります。
  • そこで、CacheManagerをオーバーライドしてローカルファイルを返すようにすると、アプリ側のコードを一切変更せずにテストが可能になります。

手順概要

  1. テスト用PNGファイルをtest_resourcesディレクトリに配置。
  2. BaseCacheManagerを継承したTestCacheManagerを作成。
  3. ゴールデンテスト内でcacheManagerパラメータにTestCacheManager()を指定。

ディレクトリ構成例

project_root/
lib/
test/
widgets/
- my_widget_golden_test.dart
test_resources/
- sample.png
golden/
- my_widget.png

実装例

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';

/// テスト用のCacheManager
class TestCacheManager extends BaseCacheManager {
  final String localImagePath;

  TestCacheManager(this.localImagePath) : super('testCache');

  @override
  Future<File> getSingleFile(String url, {Map<String, String>? headers}) async {
    // ネットワークアクセスせずに、ローカルファイルを返す
    return File(localImagePath);
  }
}

void main() {
  testWidgets('CachedNetworkImage ゴールデンテスト', (tester) async {
    final testImagePath = 'test/test_resources/sample.png';

    final widgetUnderTest = MaterialApp(
      home: CachedNetworkImage(
        imageUrl: 'https://example.com/image.png', // ダミーURL
        cacheManager: TestCacheManager(testImagePath),
        placeholder: (context, url) => const CircularProgressIndicator(),
        errorWidget: (context, url, error) => const Icon(Icons.error),
      ),
    );

    await tester.pumpWidget(widgetUnderTest);
    await tester.pumpAndSettle();

    await expectLater(
      find.byType(CachedNetworkImage),
      matchesGoldenFile('golden/my_widget.png'),
    );
  });
}

以上。

Discussion