哈嘍,我是老劉
這兩天發現一個Flutter 3.24版本的單元測試的一個小bug,提醒大家注意一下。
老劉自己寫程式碼十多年了,寫Flutter也6年多了,沒想到前兩天在一個小小的BottomNavigationBar 元件上翻了車。
給大家分享一下事件的經過。
問題經過
這件事的起因是最近想做一個自己用的小App,Flutter實現。
大家知道我一直是TDD的踐行者,所以就先寫了首頁的測試程式碼。
首頁底部設計有兩個tab按鈕,我的這個測試是點選按鈕切換頁面內容。
實現方法是BottomNavigationBar 配合頁面內容的TabView。
結果在執行tester.tap方法的地方測試程式碼就崩了。
就只有一句提示
大家注意
No tests match "xxx.."
這種提示,如果經排查發現,測試程式碼中加入某個操作就出現,那麼就可以懷疑一下是測試框架底層崩潰導致的。
我當時第一反應是不是我的BottomNavigationBar 的用法有問題?
因為之前是不用這個元件的,我們有自己封裝的UI元件庫,這次是圖省事想簡單看一下效果。
但是反覆排查也沒發現問題,而且真機執行也沒問題。
這時候我還是懷疑我自己哪個地方沒用對,於是把官方的元件測試程式碼拿過來試了一下。
testWidgets('BottomNavigationBar callback test', (WidgetTester tester) async {
late int mutatedIndex;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
onTap: (int index) {
mutatedIndex = index;
},
),
),
),
);
await tester.tap(find.text('Alarm'));
expect(mutatedIndex, 1);
});
結果也崩了。
這時候已經是凌晨1點多了,腦子有點不清醒。
本來打算去睡覺明天再說,可是突然靈光一閃,是不是我手欠選擇了Flutter 3.24.5版本的問題?
於是換成3.10,結果沒問題了,測試正常透過。
然後我就清醒了,感覺問題不是出在BottomNavigationBar上,有可能所有點選事件都有問題。
於是我又寫了一個簡單的測試程式碼:
testWidgets('Button tap test', (WidgetTester tester) async {
int ex = 0;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: TextButton(child: const Text('Alarm'), onPressed: () {
ex = 1;
}),
),
),
),
);
await tester.tap(find.text('Alarm'));
expect(ex, 1);
});
果不其然,也崩了。
所以應該是Flutter 3.24版本下,test環境,點選動作有問題。
這時候凌晨兩點多,已經困的不行了,睡覺,明天再說。
問題確定
第二天起來理了理思路,在想應該先排除一下我的本地環境問題,於是在我的筆記本上又嘗試了一次。
筆記本也復現了這個問題,而且幸運的是筆記本上海多了一個錯誤提示:
[ERROR:flutter/impeller/runtime_stage/runtime_stage.cc(28)] Reached unreachable code.
這個提示怎麼看起來是impeler的問題呢?
上網搜了一下,果然有人報了這個bug
[ERROR:flutter/impeller/runtime_stage/runtime_stage.cc(28)] Reached unreachable code. · Issue #147551 · flutter/flutter · GitHub
而且他是在mac上,Flutter版本是3.22
我本地測試了一下Flutter 3.19,是沒問題的,所以問題大機率出在3.20之後。
問題的具體細節和原因沒有花時間細看。
這裡一方面給大家一個提示,碰到這個問題大概知道是咋回事。
另一方面也是藉著這個定位過程總結一下經驗。
總結經驗
1、當執行的框架,特別是框架的底層程式碼比如c++程式碼崩潰,就很容易出現一些奇怪的現象誤導開發者。
比如這次的提示 【No tests match "xxx.."】
2、如果懷疑是test框架的問題,可以透過命令列執行並加入一些類似“-v”的引數檢視執行更詳細的過程。
這樣可以幫你快速判斷問題來源。
話說自己的課程裡還經常提醒這一點,結果用到的時候就沒想起來。
我是直到第二天用筆記本執行看到異常提示資訊才想起來,這個屬實有點不應該。
3、關於TDD中的UI測試,老劉一貫的觀點是隻測試UI中涉及業務邏輯的部分,不測試UI佈局和互動的細節。
就好像這次的測試例:'使用者點選個人中心按鈕,頁面內容切換為個人中心'
它測試的其實是如何實現一個UI效果,這部分內容是否應該透過測試覆蓋,應該覆蓋到什麼程度其實是值得商榷的。
比如如果我透過DefaultTabController + TabView + TabBar來實現這個效果,那其實測試的就是DefaultTabController的內部邏輯了。
好了,以上這些基於這次這個問題的思考才是我這篇文章想要表達的核心內容。
如果大家有更多的想法或者不同意見都歡迎交流。
點選免費領老劉整理的《Flutter開發手冊》,覆蓋90%應用開發場景。
可以作為Flutter學習的知識地圖。
覆蓋90%開發場景的《Flutter開發手冊》