過去幾天,快速開發了許多核心小功能,尤其是語音轉文字筆記。當功能變得越來越複雜,程式碼庫不斷增長時,一個至關重要的問題便浮現出來:如何確保我們今天新增的功能,不會無意間弄壞昨天寫好的程式?
答案就是做「自動化測試」。今天會暫停開發新功能,轉而為我們的App建立第一道防線 —— 單元測試(Unit Test)。我們的目標是為先前建立的Repository和Provider,特別是處理核心邏輯的NoteProvider和FavoriteProvider撰寫測試案例。
一、為何需要單元測試?它與Mock的角色
單元測試是針對程式中最小可測試單元(如一個函式或一個類別)進行的測試。它的核心思想是「隔離」,在不受外界干擾的情況下,驗證這個小單元是否如預期般運作。
而在測試Provider時,我們會遇到一個問題:NoteProvider的功能依賴於NoteRepository,而 NoteRepository又依賴於Firestore。在單元測試中,我們只想測試NoteProvider本身的邏輯,並不想真的去讀寫Firestore資料庫。
這就是模擬(Mock)技術的重要性。我們將使用mocktail這個套件來建立一個假的Repository,這個假物件會完全模仿真實Repository的行為,讓我們可以精準控制它的回傳值,從而專心測試Provider的邏輯是否正確。
二、實戰:為FavoriteProvider撰寫測試
我們先從相對簡單的FavoriteProvider開始,假設它有一個toggleFavorite方法,用於新增或移除收藏。
Step1. Arrange (安排):
Step2. Act (執行):
呼叫favoriteProvider.toggleFavorite('some_insight_id')。
Step3. Assert (斷言):
測試程式碼示意:
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
// 假設這是你的 Repository 和 Provider
class FavoriteRepository {
Future<void> toggleFavorite(String insightId) async { /* ... */ }
}
class FavoriteProvider {
final FavoriteRepository _repo;
FavoriteProvider(this._repo);
Future<void> toggle(String id) async {
await _repo.toggleFavorite(id);
}
}
// 建立 Mock Class
class MockFavoriteRepository extends Mock implements FavoriteRepository {}
void main() {
group('FavoriteProvider Tests', () {
late FavoriteRepository mockRepository;
late FavoriteProvider favoriteProvider;
setUp(() {
mockRepository = MockFavoriteRepository();
favoriteProvider = FavoriteProvider(mockRepository);
});
test('toggle should call repository correctly', () async {
// Arrange
when(() => mockRepository.toggleFavorite(any())).thenAnswer((_) async {});
// Act
await favoriteProvider.toggle('test_id');
// Assert
verify(() => mockRepository.toggleFavorite('test_id')).called(1);
});
});
}
三、挑戰:測試NoteProvider的新增筆記功能
NoteProvider撰寫測試,要先測試addNote方法,確保它能正確地呼叫Repository並處理相關狀態。
測試情境:
透過這種方式,可以確保Provider內的業務邏輯是健全的,無論底層的資料庫發生什麼變化。
單元測試只解決了「零件」的問題。這些獨立測試過的零件組合在一起後,能否順暢地協同工作呢?明天將探討整合測試 (Integration Test),將模擬從App啟動到使用者完成一個完整操作的端到端流程。
【哈囉你好:)感謝你的閱讀!其他我會常出沒的地方:Threads】