在某些情況下,單元測(cè)試依賴(lài)的類(lèi)可能從實(shí)時(shí)Web服務(wù)或數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)。這時(shí)可能很不方便,原因如下:
- 調(diào)用實(shí)時(shí)服務(wù)或數(shù)據(jù)庫(kù)會(huì)減慢測(cè)試的執(zhí)行速度。
- 如果Web服務(wù)或數(shù)據(jù)庫(kù)返回意外結(jié)果,則曾經(jīng)通過(guò)的測(cè)試可能會(huì)失敗。這被稱(chēng)為“不可靠的測(cè)試”。
- 很難使用實(shí)時(shí)Web服務(wù)或數(shù)據(jù)庫(kù)測(cè)試所有可能的成功和失敗場(chǎng)景。
所以,與其依賴(lài)實(shí)時(shí)的web服務(wù)或者數(shù)據(jù)庫(kù),不如你“mock”這些依賴(lài)。mock允許我們模擬一個(gè)實(shí)時(shí)的web服務(wù)或者數(shù)據(jù)庫(kù)并且根據(jù)情況返回特定結(jié)果。
一般來(lái)說(shuō),你可以通過(guò)創(chuàng)建類(lèi)的替代實(shí)現(xiàn)來(lái)模擬依賴(lài)項(xiàng)。你可以自己寫(xiě)這些替代實(shí)現(xiàn)或者使用更便捷的Mockito package。
下面的步驟講解了使用Mockito package
的基礎(chǔ)操作,更多操作請(qǐng)查看Mockito package documentation。
步驟
- 添加
mockito
和test
依賴(lài)。 - 創(chuàng)建一個(gè)方法用來(lái)測(cè)試。
- 創(chuàng)建一個(gè)模擬
http.Client
的測(cè)試文件。 - 為每個(gè)條件編寫(xiě)測(cè)試。
- 運(yùn)行測(cè)試。
1. 添加 mockito
和 test
依賴(lài)。
要想使用 mockito package
,你首先需要添加它和 flutter_test
到 pubspec.yaml
文件里,添加位置在dev_dependencies下面。
你也可以使用 http package
,在dependencies下面添加該依賴(lài)即可。
dependencies:
http: <newest_version>
dev_dependencies:
test: <newest_version>
mockito: <newest_version>
2. 創(chuàng)建一個(gè)方法用來(lái)測(cè)試。
在本例中,你將對(duì)從Internet方法獲取數(shù)據(jù)的fetchpost函數(shù)進(jìn)行單元測(cè)試。為了測(cè)試這個(gè)函數(shù),你需要做如下2點(diǎn)改變:
- 提供一個(gè)http.Client參數(shù)給函數(shù)。這允許你根據(jù)情況提供正確的http.Client。對(duì)于Flutter和服務(wù)端項(xiàng)目,你可以提供http.IOClient。對(duì)于瀏覽器應(yīng)用程序,你可以提供http.BrowserClient。對(duì)于單元測(cè)試,你可以提供模擬的http.Client。
2.使用提供的client從網(wǎng)絡(luò)獲取數(shù)據(jù),而不是直接使用http.get方法,否則會(huì)很難模擬數(shù)據(jù)。
這個(gè)測(cè)試函數(shù)看起來(lái)應(yīng)該是這樣的:
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. 創(chuàng)建一個(gè)模擬http.Client
的測(cè)試文件。
下一步,創(chuàng)建一個(gè)測(cè)試文件和一個(gè) MockClient
類(lèi)。根據(jù)單元測(cè)試介紹中的建議,在根目錄的 test
文件夾下創(chuàng)建一個(gè)叫 fetch_post_test.dart
的文件。
這個(gè) MockClient
類(lèi)實(shí)現(xiàn)了 http.Client
。這將允許你將 MockClient
作為參數(shù)傳遞到 fetchPost
函數(shù),并且允許你在每個(gè)測(cè)試?yán)锓祷夭煌慕Y(jié)果。
// 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. 為每個(gè)條件編寫(xiě)測(cè)試。
如果你思考一下 fetchPost
函數(shù),會(huì)想到它只能返回下面的2個(gè)結(jié)果中的一個(gè):
- 如果獲取數(shù)據(jù)成功,會(huì)返回一個(gè)Post數(shù)據(jù)。
- 如果獲取數(shù)據(jù)失敗,會(huì)拋出一個(gè)異常。
因此,你想要測(cè)試這兩個(gè)結(jié)果。你可以使用 MockClient
返回獲取數(shù)據(jù)成功的測(cè)試結(jié)果,也可以返回一個(gè)獲取數(shù)據(jù)失敗的測(cè)試結(jié)果。
為了實(shí)現(xiàn)這一點(diǎn),我們使用Mockito
提供的when
函數(shù)。
// 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. 運(yùn)行測(cè)試。
既然你現(xiàn)在寫(xiě)好了fetchPost
的單元測(cè)試,那么就可以運(yùn)行它了。
dart test/fetch_post_test.dart
你也可以使用單元測(cè)試介紹里介紹過(guò)的你喜歡的編譯器里運(yùn)行測(cè)試
總結(jié):
在這個(gè)例子里,你已經(jīng)學(xué)會(huì)了如何使用Mockito
去測(cè)試依賴(lài)web服務(wù)器或者數(shù)據(jù)庫(kù)的函數(shù)或者類(lèi)。這只是一個(gè)簡(jiǎn)短的 Mockito library
和模擬概念的介紹。更多信息請(qǐng)查看 Mockito package。