在测试运行器完成之前如何测试返回 Future
的方法?我的问题是单元测试运行器在异步方法完成之前就已经完成了。
在测试运行器完成之前如何测试返回 Future
的方法?我的问题是单元测试运行器在异步方法完成之前就已经完成了。
如何使用completion
匹配器进行测试的完整示例如下。
import 'package:unittest/unittest.dart';
class Compute {
Future<Map> sumIt(List<int> data) {
Completer completer = new Completer();
int sum = 0;
data.forEach((i) => sum += i);
completer.complete({"value" : sum});
return completer.future;
}
}
void main() {
test("testing a future", () {
Compute compute = new Compute();
Future<Map> future = compute.sumIt([1, 2, 3]);
expect(future, completion(equals({"value" : 6})));
});
}
在这段代码完成之前,单元测试运行器可能无法完成。因此,看起来单元测试执行正确。对于需要更长时间才能完成的Future
,正确的方法是利用unittest
包中提供的completion
匹配器。
/**
* Matches a [Future] that completes succesfully with a value that matches
* [matcher]. Note that this creates an asynchronous expectation. The call to
* `expect()` that includes this will return immediately and execution will
* continue. Later, when the future completes, the actual expectation will run.
*
* To test that a Future completes with an exception, you can use [throws] and
* [throwsA].
*/
Matcher completion(matcher) => new _Completes(wrapMatcher(matcher));
以下是一种错误的在Dart中对返回的Future进行单元测试的方法,会引起人们的诱惑。 警告:以下是测试Futures的一种错误方式。
以下是一种错误的在Dart中对返回的Future进行单元测试的方法,会引起人们的诱惑。 警告:以下是测试Futures的一种错误方式。
import 'package:unittest/unittest.dart';
class Compute {
Future<Map> sumIt(List<int> data) {
Completer completer = new Completer();
int sum = 0;
data.forEach((i) => sum+=i);
completer.complete({"value":sum});
return completer.future;
}
}
void main() {
test("testing a future", () {
Compute compute = new Compute();
compute.sumIt([1, 2, 3]).then((Map m) {
Expect.equals(true, m.containsKey("value"));
Expect.equals(6, m["value"]);
});
});
}
作为一种替代方案,这是我一直在做的。它与上面的答案类似:
test('get by keys', () {
Future future = asyncSetup().then((_) => store.getByKeys(["hello", "dart"]));
future.then((values) {
expect(values, hasLength(2));
expect(values.contains("world"), true);
expect(values.contains("is fun"), true);
});
expect(future, completes);
});
我获取了一个对未来的引用,并将所有期望语句放在then
调用内。然后,我注册expect(future, completes)
以确保它实际上完成。
我获取一个Future对象的引用,并将所有期望的语句放在then
函数中。接着,我使用expect(future, completes)
注册一个完成的回调函数来确保该Future对象会成功完成。
另一个可能性是使用expectAsync1函数。对于测试的初始不正确变体,可以使用工作类似的方式:
void main() {
test("testing a future", () {
Compute compute = new Compute();
compute.sumIt([1, 2, 3]).then(expectAsync1((Map m) {
Expect.equals(true, m.containsKey("value"));
Expect.equals(6, m["value"]);
}));
});
}
使用expectAsync1进行异步测试的一个优点是它的可组合性。有时测试自然需要几个顺序执行的异步代码块。
mongo_db的示例测试:
testCursorGetMore(){
var res;
Db db = new Db('${DefaultUri}mongo_dart-test');
DbCollection collection;
int count = 0;
Cursor cursor;
db.open().chain(expectAsync1((c){
collection = db.collection('new_big_collection2');
collection.remove();
return db.getLastError();
})).chain(expectAsync1((_){
cursor = new Cursor(db,collection,where.limit(10));
return cursor.each((v){
count++;
});
})).chain(expectAsync1((dummy){
expect(count,0);
List toInsert = new List();
for (int n=0;n < 1000; n++){
toInsert.add({"a":n});
}
collection.insertAll(toInsert);
return db.getLastError();
})).chain(expectAsync1((_){
cursor = new Cursor(db,collection,where.limit(10));
return cursor.each((v)=>count++);
})).then(expectAsync1((v){
expect(count,1000);
expect(cursor.cursorId,0);
expect(cursor.state,Cursor.CLOSED);
collection.remove();
db.close();
}));
}
更新:
自问题最初提出以来,Future
和unittest
API都发生了变化。现在可以从测试函数中返回 Future
,并且unittest
会正确执行所有异步保护功能。与Future
的chain
和then
方法现已合并,为具有多个连续代码块的测试提供了漂亮的语法。在当前版本的mongo_dart中,同样的测试看起来像:
Future testCursorGetMore(){
var res;
Db db = new Db('${DefaultUri}mongo_dart-test');
DbCollection collection;
int count = 0;
Cursor cursor;
return db.open().then((c){
collection = db.collection('new_big_collection2');
collection.remove();
return db.getLastError();
}).then((_){
cursor = new Cursor(db,collection,where.limit(10));
return cursor.forEach((v){
count++;
});
}).then((dummy){
expect(count,0);
List toInsert = new List();
for (int n=0;n < 1000; n++){
toInsert.add({"a":n});
}
collection.insertAll(toInsert);
return db.getLastError();
}).then((_){
cursor = new Cursor(db,collection,null);
return cursor.forEach((v)=>count++);
}).then((v){
expect(count,1000);
expect(cursor.cursorId,0);
expect(cursor.state,State.CLOSED);
collection.remove();
return db.close();
});
}
setUp()
和tearDown()
需要执行一些异步代码以便测试在setUp()
完成之前不被执行时,只需返回一个future即可。 - Günter Zöchbauer测试返回Future的方法有三个步骤:
expectLater
代替expect
,并使用await
等待。completion
包装期望值,如下所示:await expectLater(getSum(2,3), completion(5));
要测试计算总和的方法:
Future<int> getSum(int a,int b) async{
return a+b;
}
我们可以这样编写测试:
test("test sum",() async{
await expectLater(getSum(2,3), completion(5));
});
对于 mockito
版本 2+,有一种方法可以帮助您实现此操作。
await untilCalled(mockObject.someMethod())
import 'package:unittest/unittest.dart';
checkProgress() => print('Check progress called.');
main() {
test('Window timeout test', () {
var callback = expectAsync(checkProgress);
new Timer(new Duration(milliseconds:100), callback);
});
}
在测试过程中等待未来完成的另一种方法是从传递给测试函数的闭包中返回它。请参阅上面链接文章中的示例:
import 'dart:async';
import 'package:unittest/unittest.dart';
void main() {
test('test that time has passed', () {
var duration = const Duration(milliseconds: 200);
var time = new DateTime.now();
return new Future.delayed(duration).then((_) {
var delta = new DateTime.now().difference(time);
expect(delta, greaterThanOrEqualTo(duration));
});
});
}