Как мне проверить будущее в дартс? - программирование
Подтвердить что ты не робот

Как мне проверить будущее в дартс?

Как можно протестировать метод, который возвращает Future до завершения тестового бегуна? У меня проблема, когда мой бегун unit test завершается до завершения асинхронных методов.

4b9b3361

Ответ 1

Полный пример тестирования с помощью 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})));
  });
}

Бегун unit test может не завершиться до завершения этого кода. Таким образом, казалось бы, что unit test выполняется правильно. С Future, который может потребовать более длительные периоды времени, чтобы закончить правильный путь, следует использовать 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));

Возникло бы желание сделать следующее, что было бы неправильным способом модульного тестирования возвращаемого будущего в дротике. ПРЕДУПРЕЖДЕНИЕ: ниже приведен неверный способ проверки фьючерсов.

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"]);
    });
  });
}

Ответ 2

Другая возможность - использовать функцию 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();
  }));
}

Обновление:

Оба API будущего и unittest были изменены с тех пор, как был задан вопрос. Теперь можно просто вернуть 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();
  });
}

Ответ 3

В качестве альтернативы, вот что я делаю. Это похоже на ответы выше:

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), чтобы убедиться, что он действительно завершен.

Ответ 4

См. раздел асинхронных тестов в article или документации API для expectAsync.

Ниже приведен краткий пример. Обратите внимание, что expectAsync() должен быть вызван до того, как закрытие передано в test().

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));
    });
  });
}

Ответ 5

Для mockito v. 2+ Существует возможность сделать это с помощью

await untilCalled(mockObject.someMethod())