[英]How to mock a static method in Flutter with Mockito?
我有一個文件 function fetchPosts()
負責從服務器獲取新帖子並將它們存儲在本地 sqlite 數據庫中。
按照 sqflite doc的建議,我將單個 ref 存儲到我的數據庫中。
這是我的database.dart
的內容。dart 文件:
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DBProvider {
DBProvider._();
static final DBProvider db = DBProvider._();
static Database _database;
static Future<Database> get database async {
if (_database != null) return _database;
// if _database is null, we instantiate it
_database = await _initDB();
return _database;
}
static Future<Database> _initDB() async {
final dbPath = await getDatabasesPath();
String path = join(dbPath, 'demo.db');
return await openDatabase(path, version: 1, onCreate: _onCreate);
}
static Future<String> insert(String table, Map<String, dynamic> values) async { /* insert the record*/ }
// Other functions like update, delete etc.
}
然后我在我的fetchPosts.dart
文件中使用它
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../services/database.dart';
const url = 'https://myapp.herokuapp.com';
Future<void> fetchPosts() {
final client = http.Client();
return fetchPostsUsingClient(client);
}
Future<void> fetchPostsUsingClient(http.Client client) async {
final res = await client.get(url);
final posts await Post.fromJson(json.decode(response.body));
for (var i = 0; i < posts.length; i++) {
await DBProvider.insert('posts', posts[i]);
}
}
在我的測試中,如何verify
DBProvider.insert()
是否已被調用?
fetchPosts_test.dart
import 'package:test/test.dart';
import 'package:http/http.dart' as http;
import 'package:mockito/mockito.dart';
import 'package:../services/fetchPosts.dart';
// 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 {}
void main() {
group('fetchPosts', () {
test('update local db', () async {
final client = MockClient();
// Use Mockito to return a successful response when it calls the provided http.Client.
when(client.get()).thenAnswer((_) async => http.Response('{"title": "Test"}', 200));
await fetchPostsWithClient(client);
verify(/* DBProvider.insert has been called ?*/);
});
});
}
最終,我不得不重寫我的database.dart
以使其可測試/可模擬。
這是新文件:
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DBProvider {
static final DBProvider _singleton = DBProvider._internal();
factory DBProvider() {
return _singleton;
}
DBProvider._internal();
static Database _db;
static Future<Database> _getDatabase() async {
if (_db != null) return _db;
// if _database is null, we instantiate it
_db = await _initDB();
return _db;
}
static Future<Database> _initDB() async {
final dbPath = await getDatabasesPath();
String path = join(dbPath, 'demo.db');
return openDatabase(path, version: 1, onCreate: _onCreate);
}
Future<String> insert(String table, Map<String, dynamic> values) async {
final db = await _getDatabase();
return db.insert(table, values);
}
// ...
}
現在我可以使用與 http.Client 相同的技巧。 謝謝@RémiRousselet
假設我們要測試 [TargetClass.someMethodCallOtherStaticMethod]
Class StaticMethodClass {
static int someStaticMethod() {};
}
Class TargetClass {
int someMethodCallOtherStaticMethod() {
return StaticMethodClass.someStaticMethod();
}
}
我們應該重構 [[TargetClass.someMethodCallOtherStaticMethod]] 進行測試,如下所示:
Class TargetClass {
int someMethodCallOtherStaticMethod({@visibleForTesting dynamic staticMethodClassForTesting}) {
if (staticMethodClassForTesting != null) {
return staticMethodClassForTesting.someStaticMethod();
} else {
return StaticMethodClass.someStaticMethod();
}
}
}
現在您可以像這樣編寫測試用例:
// MockClass need to implement nothing, just extends Mock
MockClass extends Mock {}
test('someMethodCallOtherStaticMethod', () {
// We MUST define `mocked` as a dynamic type, so that no errors will be reported during compilation
dynamic mocked = MockClass();
TargetClass target = TargetClass();
when(mocked.someStaticMethod()).thenAnswer((realInvocation) => 42);
expect(target.someMethodCallOtherStaticMethod(staticMethodClassForTesting: mocked), 42);
})
這個問題是不久前的,但這是另一個解決方案。 您可以重構對 static function 的調用,以便從 class “包裝器”方法調用。 這是我經常用來模擬對第三方服務的請求的模式。
讓我給你舉個例子。 為簡單起見,假設 Engine 有 3 個 static 方法需要被模擬:brake()、accelerate() 和 speed()。
class Car {
int currentSpeed;
void accelerateTo(int speed) {
while(currentSpeed > speed) {
Engine.brake();
currentSpeed = Engine.speed();
}
while(currentSpeed < speed) {
Engine.accelerate();
currentSpeed = Engine.speed();
}
}
}
現在您想模擬對引擎的所有調用,為此我們可以將代碼重構為:
class Car {
int currentSpeed;
void accelerateTo(int speed) {
while(currentSpeed > speed) {
brake();
currentSpeed = speed();
}
while(currentSpeed < speed) {
accelerate();
currentSpeed = speed();
}
}
/// wrapper to mock Engine calls during test
void brake() {
Engine.brake();
}
/// wrapper to mock Engine calls during test
int speed() {
Engine.speed();
}
/// wrapper to mock Engine calls during test
void accelerate() {
Engine.accelerate();
}
}
在集成測試中,您現在可以模擬與 static 方法直接交互的 3 個方法,但您現在可以測試您的主要方法。 雖然您也可以在這里重構引擎 class 本身,但通常 class 將在第三方服務中。
這個例子不是基於大眾汽車丑聞;)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.