在某些情況下,單元測試依賴的類可能從實時Web服務或資料庫中獲取資料。這時可能很不方便,原因如下:
- 呼叫實時服務或資料庫會減慢測試的執行速度。
- 如果Web服務或資料庫返回意外結果,則曾經通過的測試可能會失敗。這被稱為“不可靠的測試”。
- 很難使用實時Web服務或資料庫測試所有可能的成功和失敗場景。
所以,與其依賴實時的web服務或者資料庫,不如你“mock”這些依賴。mock允許我們模擬一個實時的web服務或者資料庫並且根據情況返回特定結果。
一般來說,你可以通過建立類的替代實現來模擬依賴項。你可以自己寫這些替代實現或者使用更便捷的Mockito package。
下面的步驟講解了使用Mockito package
的基礎操作,更多操作請檢視Mockito package documentation。
步驟
- 新增
mockito
和test
依賴。 - 建立一個方法用來測試。
- 建立一個模擬
http.Client
的測試檔案。 - 為每個條件編寫測試。
- 執行測試。
1. 新增 mockito
和 test
依賴。
要想使用 mockito package
,你首先需要新增它和 flutter_test
到 pubspec.yaml
檔案裡,新增位置在dev_dependencies下面。
你也可以使用 http package
,在dependencies下面新增該依賴即可。
dependencies:
http: <newest_version>
dev_dependencies:
test: <newest_version>
mockito: <newest_version>
複製程式碼
2. 建立一個方法用來測試。
在本例中,你將對從Internet方法獲取資料的fetchpost函式進行單元測試。為了測試這個函式,你需要做如下2點改變:
- 提供一個http.Client引數給函式。這允許你根據情況提供正確的http.Client。對於Flutter和服務端專案,你可以提供http.IOClient。對於瀏覽器應用程式,你可以提供http.BrowserClient。對於單元測試,你可以提供模擬的http.Client。
2.使用提供的client從網路獲取資料,而不是直接使用http.get方法,否則會很難模擬資料。
這個測試函式看起來應該是這樣的:
Future<Post> fetchPost(http.Client client) async {
final response =
await client.get('https://jsonplaceholder.typicode.com/posts/1');
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
return Post.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
複製程式碼
3. 建立一個模擬http.Client
的測試檔案。
下一步,建立一個測試檔案和一個 MockClient
類。根據單元測試介紹中的建議,在根目錄的 test
資料夾下建立一個叫 fetch_post_test.dart
的檔案。
這個 MockClient
類實現了 http.Client
。這將允許你將 MockClient
作為引數傳遞到 fetchPost
函式,並且允許你在每個測試裡返回不同的結果。
// Create a MockClient using the Mock class provided by the Mockito package.
// Create new instances of this class in each test.
class MockClient extends Mock implements http.Client {}
main() {
// Tests go here
}
複製程式碼
4. 為每個條件編寫測試。
如果你思考一下 fetchPost
函式,會想到它只能返回下面的2個結果中的一個:
- 如果獲取資料成功,會返回一個Post資料。
- 如果獲取資料失敗,會丟擲一個異常。
因此,你想要測試這兩個結果。你可以使用 MockClient
返回獲取資料成功的測試結果,也可以返回一個獲取資料失敗的測試結果。
為了實現這一點,我們使用Mockito
提供的when
函式。
// Create a MockClient using the Mock class provided by the Mockito package.
// Create new instances of this class in each test.
class MockClient extends Mock implements http.Client {}
main() {
group('fetchPost', () {
test('returns a Post if the http call completes successfully', () async {
final client = MockClient();
// Use Mockito to return a successful response when it calls the
// provided http.Client.
when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
.thenAnswer((_) async => http.Response('{"title": "Test"}', 200));
expect(await fetchPost(client), isInstanceOf<Post>());
});
test('throws an exception if the http call completes with an error', () {
final client = MockClient();
// Use Mockito to return an unsuccessful response when it calls the
// provided http.Client.
when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
.thenAnswer((_) async => http.Response('Not Found', 404));
expect(fetchPost(client), throwsException);
});
});
}
複製程式碼
5. 執行測試。
既然你現在寫好了fetchPost
的單元測試,那麼就可以執行它了。
dart test/fetch_post_test.dart
複製程式碼
你也可以使用單元測試介紹裡介紹過的你喜歡的編譯器裡執行測試
總結:
在這個例子裡,你已經學會了如何使用Mockito
去測試依賴web伺服器或者資料庫的函式或者類。這只是一個簡短的 Mockito library
和模擬概念的介紹。更多資訊請檢視 Mockito package。