如何测试Future
在测试运行器完成之前返回的方法?我有一个问题,我的单元测试运行器在异步方法完成之前完成。
6 回答
如何使用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
s 可能需要更长的时间才能完成正确的方法是利用completion
unittest 包中可用的匹配器。
/**
* 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 进行单元测试的不正确方法。警告:以下是测试期货的不正确方法。
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"]);
});
});
}
另一种可能性是使用 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 使用所有异步保护功能正确执行它。结合现在合并的事实chain
和then
方法,为具有几个顺序代码块的测试提供了很好的语法。Future
在当前版本的 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();
});
}
作为替代方案,这就是我一直在做的事情。它类似于上面的答案:
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
调用中。然后,我注册 aexpect(future, completes)
以确保它真正完成。
测试返回 Future 的方法的 3 个步骤:
- 使测试异步
- 而不是
expect
,使用expectLater
和await
它。 completion
传入未来的方法/getter,并用这样的包装期望值:
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));
});
请参阅本文中有关异步测试的部分,或expectAsync的 API 文档。
下面是一个简短的例子。请注意,必须在传递给 test() 的闭包返回之前调用 expectAsync()。
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));
});
});
}
对于mockito
v. 2+ 有可能在
await untilCalled(mockObject.someMethod())