ข้ามไปยังเนื้อหา

Widget Testing

ใน React คุณทดสอบ components ด้วย jest และ React Testing Library: render() mount component, screen.getByText() ใช้ query, fireEvent.click() ใช้โต้ตอบ Testing API ของ Flutter แมปแทบหนึ่งต่อหนึ่ง: testWidgets() คือ block test() ของคุณ, pumpWidget() คือ render(), find.text() คือ screen.getByText(), และ tester.tap() คือ fireEvent.click() mental model เหมือนกันทุกประการ — แค่ syntax ต่างกัน

หมายเหตุเกี่ยวกับการทดสอบ: Widget tests ต้องใช้ runner ของ flutter test — ไม่สามารถรันใน DartPad ได้ รันในเครื่องด้วย flutter test

Flutter tests อยู่ใน test/ ที่ root ของโปรเจกต์ ซึ่งสอดคล้องกับวิธีที่คุณอาจวาง tests ไว้ใน src/__tests__/ หรือ colocate ไฟล์ *.test.tsx naming convention คือ *_test.dart — test runner ของ Flutter จะค้นหาไฟล์ที่ลงท้ายด้วย _test.dart ใน test/ โดยอัตโนมัติ

รัน tests ด้วย:

Terminal window
# Run all tests
flutter test
# Run a single file
flutter test test/counter_test.dart

package flutter_test ฝังอยู่ใน SDK แล้ว — ไม่ต้องใช้ npm install นำเข้าในทุกไฟล์ทดสอบ:

import 'package:flutter_test/flutter_test.dart';

ห่อ widget ที่ทดสอบด้วย MaterialApp(home: Scaffold(body: ...)) นี่คือ Flutter equivalent ของการห่อ component ใน Provider, ThemeProvider, หรือ custom render wrapper ใน RTL — wrapper นี้ทำให้ widget ที่อ่าน Theme, MediaQuery, หรือ Navigator จาก ancestor context ทำงานได้

React Testing LibraryFlutter widget testวัตถุประสงค์
render(<MyWidget />)await tester.pumpWidget(const MaterialApp(home: MyWidget()))Mount widget
screen.getByText('Hi')find.text('Hi')ค้นหาด้วย text
screen.getByType(Button)find.byType(ElevatedButton)ค้นหาด้วย type
screen.getByKey('my-key')find.byKey(const Key('my-key'))ค้นหาด้วย key
fireEvent.click(el)await tester.tap(find.byType(ElevatedButton))Tap
userEvent.type(el, 'Hello')await tester.enterText(find.byType(TextField), 'Hello')พิมพ์ text
await waitFor(...)await tester.pumpAndSettle()รอ animations/async
expect(el).toBeInTheDocument()expect(find.text('Hi'), findsOneWidget)ยืนยันการมีอยู่
expect(el).not.toBeInTheDocument()expect(find.text('Hi'), findsNothing)ยืนยันการไม่มีอยู่
React + RTL
// React + React Testing Library
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
test('increments count on button click', () => {
render(<Counter />);
expect(screen.getByText('Count: 0')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: /increment/i }));
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
Flutter testWidgets
// Flutter testWidgets
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/counter.dart';
void main() {
testWidgets('increments count on button tap', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(home: Scaffold(body: Counter())),
);
expect(find.text('Count: 0'), findsOneWidget);
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
expect(find.text('Count: 1'), findsOneWidget);
});
}
Flutter equivalent ของฟังก์ชัน render() ของ RTL คืออะไร?
await tester.pumpAndSettle() ทำอะไร?
matcher ใดยืนยันว่า widget ไม่มีอยู่ใน tree?
ทำไม widgets ที่ทดสอบต้องถูกห่อใน MaterialApp?