iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0
Mobile Development

30 天輕鬆學會 Flutter 測試系列 第 20

Day 20 再多的文字都比不上一張圖片

  • 分享至 

  • xImage
  •  

在開發 Flutter 程式的過程中,或多或少都會使用圖片或 Icon 來增加使用者體驗,畢竟人都是視覺動物,比起文字,豐富的圖片與動畫特效更加吸人眼球。今天就來看看在 Widget Test 中,在測試圖片時會遇到什麼問題吧。一樣讓我們先舉個簡單的例子。

舉個例子

我們來看個簡單的程式,在程式中用圖片呈現葉問 2 的經典場景,畫面一開始顯示了葉問叫洪師傅切對手中路,然後使用者畫面中的按鈕看看洪師父怎麼回應。[範例程式]

1.jpg

那我們想驗證假設我想驗證按鈕的功能是否正常要怎麼測呢?想必觀眾朋友應該都已經熟練了,我們可以驗證洪師父的圖片有沒有有沒有出現在畫面上。

main() {
  testWidgets("show master hong's reply", (tester) async {
      await tester.pumpWidget(const IpMan());

      await tester.tap(find.text("看看洪師父怎麼說"));

      // 驗證洪師父圖片
  });
}

來到 3A 的最後一步,我們要怎麼驗證圖片呢?如果我們搜尋 Finder,可以找到 find.image 這個 Finder,看起來應該就是要用它了,再看看他的參數是 ImageProvider,如果我們再移至 ImageProvider 的定義,用 IDE 找到繼承 ImageProvider,就能找到一係列可能選擇。

2.png

在範例中,我們使用 Image.asset() 來顯示圖片,相對應的在測試中的我們就得使用 AssetImage 了。

main() {
  testWidgets("show master hong's reply", (tester) async {
    await tester.pumpWidget(const IpMan());

    await tester.tap(find.text("看看洪師父怎麼說"));
    await tester.pump();

    expect(find.image(const AssetImage("assets/master_hong.jpg")), findsOneWidget);
  });
}

最後我們執行測試,得到綠燈。

使用 flutter_gen 產生圖片路徑常數

在上面的測試中,我們在正式程式碼與測試中都是手動寫死了圖片路徑,萬一兩邊寫的不相符,我們可能一時間可能檢查不出問題。所以我們可以使用 flutter_gen 來產生這些路徑的常數,預設產生出來的常數檔放在 lib/gen 目錄中,檔名為 Assets。

3.png

然後我們就能直接在正式與測試程式碼中使用這些常數,避免打錯字的問題。

testWidgets("show master hong's reply", (tester) async {
  await tester.pumpWidget(const IpMan());

  await tester.tap(find.text("看看洪師父怎麼說"));
  await tester.pump();

  expect(find.image(AssetImage(Assets.masterHong.path)), findsOneWidget);
});

載入網路圖片

Flutter 的 Image 有許多 factory 方法,在上面的例子中,我們使用過 Image.asset(),而在眾多方法中,還有一個也是我們常用的,那就是 Image.netowrk()。在這個方法中,我們把遠端圖片網址當參數放到 Image.network() 中,在 Image 要顯示在畫面上時,Flutter 就會幫我們去讀取遠端圖片並顯示在畫面上。讓我們修改一下上面的例子,都改成使用 Image.network() 在來測試看看會發什麼事吧。[範例程式]

修改完成之後,我們測試也跟著做相應的調整,之前我們查看 ImageProvider 的子類的時候,也有看到 NetworkImage 這個類別,剛好可以用在測試上。

testWidgets("show master hong's reply", (tester) async {
  await tester.pumpWidget(const IpMan());

  await tester.tap(find.text("看看洪師父怎麼說"));
  await tester.pump();

  expect(find.image(const NetworkImage("https://raw.githubusercontent.com/easylive1989/images/master/static/images/2023IThome/Day20/master_hong.jpg")), findsOneWidget);
});

當我們改完測試並執行後會發現,測試得到了紅燈,查看錯訊息會發現,測試回報了讀取網路圖片失敗,無法正確載入葉問的圖片。

4.png

還記得我們在之前的文章中有提到,在 Widget Test 預設是會擋掉所有遠端呼叫的,讀取遠端圖片也是一種遠端呼叫,所以自然的也行不通。那我們要怎麼處理呢?

使用 errorBuilder

在 Image.network() 的參數中,有一個 errorBuilder 參數,如果我們有設定參數,當圖片載入失敗時,就會呼叫 errorBuilder 顯示圖片載入失敗的畫面。

Image.network(
  "https://raw.githubusercontent.com/easylive1989/images/master/static/images/2023IThome/Day20/ipman.jpg",
  errorBuilder: (context, error, stackTrace) => const Text("圖片載入失敗"),
)

如果們修改一下正式程式碼,在使用 Image.network() 時都加上 errorBuilder,當圖片無法載入時,也能正常顯示 Widget,測試也就能正常通過。[範例程式]

那我們還有沒有其他方式處理 Widget Test 的錯誤呢?

使用 network_image_mock 避免錯誤

我們除了使用 errorBuilder 之外,還能使用 network_image_mock 套件,這個套件能協助我們避免因為 Image.network() 讀不到圖片而造成 Widget Test 報錯。只要在呼叫 pumpWidget 時,用 mockNetworkImages 包住即可。

testWidgets("show master hong's reply", (tester) async {
  await mockNetworkImages(() => tester.pumpWidget(const IpMan()));

  await tester.tap(find.text("看看洪師父怎麼說"));
  await tester.pump();

  expect(find.image(const NetworkImage("https://raw.githubusercontent.com/easylive1989/images/master/static/images/2023IThome/Day20/master_hong.jpg")), findsOneWidget);
});

什麼時候會需要驗證圖片呢?

其實大多時候,我們都不會驗證圖片作為結果,就像開頭講的,圖片大多時候只是增加使用者體驗,而非重要的資訊。既然這樣,我們在選擇驗證的什麼時候,應該是要選擇先驗證那些畫面上的重要資訊,只有當不得已的情況,畫面中只有圖片拿來做驗證的時候,我們才會選擇用圖片驗證。

假設在建立聊天室的例子中,我們建立成功的時候不是顯示訊息,而是顯示一張圖時,若我們想從畫面檢查是否有出現成功訊息,那就只能檢查圖片了。

小結

今天介紹了跟圖片相關的議題,在驗證圖片時,我們可以使用 find.image,並根據正式程式碼使用的圖片載入方式,決定在測試中使用哪種 ImageProvider。我們也討論如何處理 Image.network() 在 Widget Test 中的錯誤與什麼時候需要驗證圖片,希望大家看完今天的文章都能都有一點收穫。


上一篇
Day 19 測試 Routing 回傳值
下一篇
Day 21 測試在 Widget 打開外部連結
系列文
30 天輕鬆學會 Flutter 測試30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
月湖 (若虛)
iT邦新手 2 級 ‧ 2023-10-04 22:19:55

嗨,保羅哥

圖片網址訪問的結果是 AccessDenied,我看不到你文章的圖片。

保羅 iT邦新手 3 級 ‧ 2023-10-05 09:38:46 檢舉

哎呀,忘記更新圖片,感謝提醒/images/emoticon/emoticon02.gif

我要留言

立即登入留言