Flutter測試(二):在專案中進行 Widget 測試

Flutter筆記發表於2019-07-15

上回書對 Flutter 中 Widget 測試的官方 Demo 進行了簡單的講解,這篇文章我們對自己的專案進行 Widget 測試。

就拿 「想吃啥」APP 來進行測試吧。

Flutter測試(二):在專案中進行 Widget 測試

在首頁中,我們可以看到有 6 個 Widget,有:

  1. 葷菜 & 素菜:
  2. 選個菜吧 ×2
  3. Button ×2

因為平時我們寫APP的時候,肯定會封裝一些 Widget 來進行復用,所以首頁中 選個菜吧 & Button 都被我封裝成了一個 Widget。

下面我們就先針對這兩個 Widget 進行測試。

選個菜吧

首先我們也是根據步驟來,先編寫 Widget,然後編寫 Widget 測試。(說得好像是廢話?)

編寫Widget

其中 「選個菜吧」是封裝了一個 Row,裡面包含了一個 Text & IconButton ,具體程式碼如下:

class MenuWidget extends StatelessWidget {

  final Stream stream;
  final VoidCallback callback;
  
  MenuWidget(this.stream, this.callback);

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        StreamBuilder(
          stream: stream,
          initialData: "選個菜吧",
          builder: (context, snapshot) {
            return Text(
              snapshot.data,
              style: TextStyle(fontSize: 34, color: Colors.black87),
            );
          },
        ),
        IconButton(
          icon: Icon(Icons.refresh),
          onPressed: callback,
        ),
      ],
    );
  }
}

複製程式碼

引數有兩個:

  1. Stream:用於 StreamBuilder 的 stream 引數,在本APP中是用來隨機選單
  2. VoidCallback:用於 IconButton 的點選事件

這樣我們就封裝成了一個 Widget,可以在編寫 UI 的時候複用了,那既然寫完了 Widget,下面就要對他進行測試了。

編寫 Widget 測試

在編寫測試程式碼之前,我們要明確該 Widget 的作用。(由於我是在寫文章,可能很多人沒仔細看前面的程式碼,所以這裡還是解釋一下該 Widget的邏輯):

  1. 該 Widget 是由兩個 Widget 組合而成。一個是 Text, 一個是 IconButton。
  2. Text 用來展示菜譜。
  3. IconButton 用來點選呼叫隨機菜譜的方法。

所以我們在寫測試的時候,也應該按照上述的邏輯來寫,我寫的測試程式碼如下:

void main() {
  testWidgets('MenuWidget test', (WidgetTester tester) async {
    StreamController<String> _controller = StreamController();
    try {
      await tester.pumpWidget(MaterialApp(
        home: Material(
          child: MenuWidget(
            _controller.stream, 
                () => _controller.sink.add("111"),
          ),
        ),
      ));
      expect(find.text('選個菜吧'), findsOneWidget);

      await tester.tap(find.byIcon(Icons.refresh));
      await tester.pump();
      expect(find.text('111'), findsOneWidget);

      
    } finally {
      _controller.close();
    }
  });
}
複製程式碼

上述邏輯大致如下:

  1. 建立測試的 Widget,也就是我們的 MenuWidget,其中需要兩個引數,一個是 stream,一個是 VoidCallback
  2. 由於 Stream 必須要 close,所以套了一層異常捕獲,在 finally 中釋放 stream。
  3. 定義好 Widget 後,查詢,是否有「選個菜吧」 的Widget。
  4. 如果有的話,則點選一個 Icon 為 Icons.refresh 的按鈕。
  5. 然後重新整理頁面
  6. 查詢是否有文字為「111」的 Widget。

執行測試程式碼,結果如下:

Flutter測試(二):在專案中進行 Widget 測試

可以看到確實是通過了,那就證明,我們組合的這個 Widget 不存在邏輯的問題,也就是該 Widget 可用。

Button

在開發中,對於 Button 樣式的一致性大家肯定是有了解的,那既然如此,就要封裝好一個通用的Button。

編寫Widget

一個 Button 需要的引數無非也就兩個:

  1. Button 上顯示的文案
  2. Button 點選回撥

程式碼如下:

class CommonButton extends StatelessWidget {
  
  final String text;
  final VoidCallback callback;

  CommonButton(this.text, this.callback);

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(10))),
      color: Theme.of(context).accentColor,
      textColor: Colors.white,
      padding: EdgeInsets.symmetric(horizontal: 50, vertical: 10),
      onPressed: callback,
      child: Text(
        text,
        style: TextStyle(fontSize: 18),
      ),
    );
  }
}
複製程式碼

這裡封裝的 Button 就是一個 RaisedButton,用了圓角,設定了文字的大小,這樣就封裝好了。

下面就寫該 Button 的測試。

編寫 Widget 測試

Button 的測試邏輯還是非常簡單的,我們只需要判斷:

  1. 文字是否正常顯示
  2. 點選回撥是否走得通

以上兩個條件就 ok 了。

測試程式碼如下:

testWidgets('CommonButton test', (WidgetTester tester) async {
  bool flag = false;
  await tester.pumpWidget(MaterialApp(
    home: Material(
      child: CommonButton(
        '我是CommonButton',
        () => flag = true,
      ),
    ),
  ));
  expect(find.text('我是CommonButton'), findsOneWidget);
  expect(flag, isFalse);
  await tester.tap(find.byType(CommonButton));
  expect(flag, isTrue);
});
複製程式碼

上述程式碼邏輯為:

  1. 定義一個 flag,用來測試 Button 的點選回撥
  2. 建立 Widget,定義好引數
  3. 判斷 Button 的文案是否正常顯示
  4. 判斷 flag 是否為開始定義的值
  5. 點選該 Button
  6. 判斷 flag 是否已經更改

執行結果如下:

Flutter測試(二):在專案中進行 Widget 測試

可以看到上述有兩個 test 都完成了。

總結

在 Flutter 中,一切皆為 Widget。

相信各位學 Flutter 的也都知道這個概念,那就可以看得出來,Widget 測試是 Flutter 中最重要的測試。

一個好的程式,測試覆蓋率應該也要很高。

Flutter測試(二):在專案中進行 Widget 測試

相關文章