繁体   English   中英

我应该如何在 Dart 中测试 Future?

[英]How should I test a Future in Dart?

如何在测试运行器完成之前测试返回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 s,可能需要更长的时间才能完成正确的方法是利用 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 进行单元测试的不正确方式。 警告:下面是测试期货的不正确方法。

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 使用所有异步保护功能正确执行它。 与事实相结合chainthen的方法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调用中。 然后,我注册一个expect(future, completes)以确保它实际完成。

看到这个异步测试的部分文章,或者API文档expectAsync

下面是一个简单的例子。 请注意,必须在传递给 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())

测试返回 Future 的方法的 3 个步骤:

  1. 使测试异步
  2. 而不是expect ,使用expectLaterawait它。
  3. 传入未来的方法/getter 并像这样用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));
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM