今日的程式碼 => GITHUB
接續上一篇 【第九天 - Flutter Bloc+Cubit 架構教學】
今天要來介紹單元測試,如何和 bloc pattern 結合,如何使用 mocktail 產生假資料。
dependencies:
mocktail: ^0.1.4
dev_dependencies:
bloc_test: ^8.1.0
MockRepoImp
,這邊是我們示範如何寫假的資料,但是我自己不喜歡這樣寫,因為這樣寫會需要寫很多很多的 Code,因此後面我們有介紹另外一種寫法,使用了強大的 mocktail套件,可以幫助我們解決這個問題。
Test
void main() {
group('這個群組的測試名稱', () {
test('測試名稱', () async{
});
});
}
// 架構 樣子
Main
----|Group
----|-----|Test
----|-----|Test
----|-----|Test
verify = 裡面會有現在 state 的狀態,然後我們就可以拿到 state 裡面的參數來做驗證了。
expect = 前面釋放程式碼結果,後面釋放預計結果。
isA<>() = <> 裡面就釋放類別。
class MockRepoImp extends IPostRepository {
@override
Future<List<PostModel>> fetchData() async => [
PostModel(
userId: 1, id: 1, title: 'Mickey title', body: 'this is the body'),
PostModel(
userId: 2, id: 2, title: 'Ruby title', body: 'this is the body'),
];
}
void main() {
group('Post Bloc Test', () {
blocTest<PostBloc, IPostState>(
'確認 FetchPostData 的狀態是對的',
build: () => PostBloc(repository: MockRepoImp()),
act: (bloc) => bloc.add(FetchPostData()),
// 設定事件的初始狀態
seed: () => PostLoading(),
// 設定 Delay 時間
wait: const Duration(milliseconds: 300),
expect: () => [
// isA<PostLoading>(), // 初始化的狀態並不會被觸發
isA<PostSuccess>(),
],
);
blocTest<PostBloc, IPostState>(
'預設 Sort by Id',
build: () => PostBloc(repository: MockRepoImp()),
act: (bloc) => bloc.add(FetchPostData()),
verify: (bloc) {
final _state = bloc.state as PostSuccess;
expect(_state.postList.length, 2);
expect(_state.postList[0].id, 1);
expect(_state.postList[1].id, 2);
},
);
//
blocTest<PostBloc, IPostState>(
'Sorted by title',
build: () => PostBloc(repository: MockRepoImp()),
act: (bloc) => bloc
..add(FetchPostData())
..add(SortPostEvent(sortState: SortState.title)),
expect: () => [
isA<PostSuccess>(),
isA<PostSuccess>(),
],
verify: (bloc) {
final _state = bloc.state as PostSuccess;
expect(_state.postList.length, 2);
expect(_state.postList[0].id, 1);
expect(_state.postList[1].id, 2);
},
);
});
}
Mock
就是模擬行為,以下面的範例為例子,我們的 PostService
有一個 function
叫做 fetchData()
,可是今天我單元測試,我想要能夠控制 fetchData
回傳的資料是什麼東西。因為這樣才可以達到後面我去驗證後面的結果。所以使用 Mock
可以讓 fetchData
回傳假的資料,回傳任何型態的資料。有點類似覆寫 fetchData
這個方法的感覺。
我們現在想要 Mock PostService 的話,我們就 extends Mock 然後 implements PostService 就可以了。
class MockPostService extends Mock implements PostService {}
這行是我們要覆寫 fetchData()
然後希望他的假資料是 _mockList
。
when(() => _post_service.fetchData()).thenAnswer((_) async => _mockList);
class MockPostService extends Mock implements PostService {}
final _mockList = [
PostModel(userId: 1, id: 1, title: 'Mickey title', body: 'this is the body'),
PostModel(userId: 2, id: 2, title: 'Ruby title', body: 'this is the body'),
];
void main() {
group('test_API', () {
final _post_service = MockPostService();
test('returns List<PostModel> and called only one time if the http call completes successfully', () async {
//Arrange
when(() => _post_service.fetchData()).thenAnswer((_) async => _mockList);
// act
final act = await _post_service.fetchData();
// assert
expect(act, isA<List<PostModel>>());
verify(() => _post_service.fetchData()).called(1);
});
});
}