Как я могу сделать асинхронный вызов в сочетании с планировщиком испытаний?
Короче говоря
command.Execute()
- наблюдаемая холодная погода,Вам нужно подписаться на него, вместо того, чтобы использовать await
.
Учитывая ваш интерес к TestScheduler
, я полагаю, вы хотите проверить что-то, вовлекающее время.Однако из раздела Когда я должен заботиться о планировании раздела :
потоками, созданными с помощью «new Thread ()» или «Task.Run», нельзя управлять в блокеtest.
Итак, если вы хотите проверить, например, что ваш Task
завершается в течение 100 мс, вам придется подождать, пока завершится асинхронный метод.Конечно, это , а не , для которого предназначен тип теста TestScheduler
.
Несколько более длинная версия
Цель TestScheduler
для проверки рабочих процессов, приводя вещи в движение и проверяя состояние в определенные моменты времени.Поскольку мы можем манипулировать временем только на TestScheduler
, вы, как правило, предпочитаете не ждать завершения реального асинхронного кода, поскольку нет способа быстрой пересылки реальных вычислений или операций ввода-вывода.Помните, речь идет о проверке рабочих процессов: vm.A
имеет новое значение в 20 мс, поэтому vm.B
должно иметь новое значение в 120 мс, ...
Так как же вы можете проверить SUT?
1 \ Вы можете смоделировать асинхронный метод, используя scheduler.CreateColdObservable
public class ViewModelTests
{
[Fact]
public void Test()
{
string observed = "";
new TestScheduler().With(scheduler =>
{
var observable = scheduler.CreateColdObservable(
scheduler.OnNextAt(100, "Done"));
observable.Subscribe(value => observed = value);
Assert.Equal("", observed);
scheduler.AdvanceByMs(99);
Assert.Equal("", observed);
scheduler.AdvanceByMs(1);
Assert.Equal("Done", observed);
});
}
}
Здесь мы в основном заменили command.Execute()
на var observable
, созданный в scheduler
.
Ясно, что приведенный выше пример довольно прост, но с несколькими наблюдаемыми, уведомляющими друг друга, этот вид теста может дать ценную информацию, а также защитную сетку при рефакторинге.
Ссылка:
2 \ Вы можете сослаться на IScheduler
явно
a) Использование планировщиков, предоставленных RxApp
public class MyViewModel : ReactiveObject
{
public string Observed { get; set; }
public MyViewModel()
{
Observed = "";
this.MyCommand = ReactiveCommand
.CreateFromTask(SomeAsyncMethod);
}
public ReactiveCommand<Unit, Unit> MyCommand { get; }
private async Task SomeAsyncMethod()
{
await RxApp.TaskpoolScheduler.Sleep(TimeSpan.FromMilliseconds(100));
Observed = "Done";
}
}
public class ViewModelTests
{
[Fact]
public void Test()
{
new TestScheduler().With(scheduler =>
{
var vm = new MyViewModel();
vm.MyCommand.Execute().Subscribe();
Assert.Equal("", vm.Observed);
scheduler.AdvanceByMs(99);
Assert.Equal("", vm.Observed);
scheduler.AdvanceByMs(1);
Assert.Equal("Done", vm.Observed);
});
}
}
Примечание
CreateFromTask
создает ReactiveCommand
с логикой асинхронного выполнения.Нет необходимости определять метод Test
как асинхронный или ожидать TestScheduler
.
В области действия With
метода расширения RxApp.TaskpoolScheduler
= RxApp.MainThreadScheduler
= new TestScheduler()
.
б) Управление собственными планировщиками с помощью инжектора конструктора
public class MyViewModel : ReactiveObject
{
private readonly IScheduler _taskpoolScheduler;
public string Observed { get; set; }
public MyViewModel(IScheduler scheduler)
{
_taskpoolScheduler = scheduler;
Observed = "";
this.MyCommand = ReactiveCommand
.CreateFromTask(SomeAsyncMethod);
}
public ReactiveCommand<Unit, Unit> MyCommand { get; }
private async Task SomeAsyncMethod()
{
await _taskpoolScheduler.Sleep(TimeSpan.FromMilliseconds(100));
Observed = "Done";
}
}
public class ViewModelTests
{
[Fact]
public void Test()
{
new TestScheduler().With(scheduler =>
{
var vm = new MyViewModel(scheduler); ;
vm.MyCommand.Execute().Subscribe();
Assert.Equal("", vm.Observed);
scheduler.AdvanceByMs(99);
Assert.Equal("", vm.Observed);
scheduler.AdvanceByMs(0);
Assert.Equal("Done", vm.Observed);
});
}
}
Ссылка:
Давайте сравним ряды с другой цитатой из Haacked:
К сожалению, и этоСледующий пункт важен, TestScheduler
не распространяется на реальную жизнь, поэтому ваши махинации ограничены вашим асинхронным реактивным кодом.Таким образом, если вы вызовете Thread.Sleep(1000)
в своем тесте, этот поток действительно будет заблокирован на секунду.Но что касается планировщика испытаний, времени не прошло.