下面的单元测试永远不会打印" Async 3"因为测试首先完成。我怎样才能确保它完成?我能想到的最好的是最后一个任意的Task.Delay或WriteAsync()。结果,都不是理想的。
public async Task TestMethod1() // eg. webjob
{
TestContext.WriteLine("Starting test...");
var observable = Observable.Create<int>(async ob =>
{
ob.OnNext(1);
await Task.Delay(1000); // Fake async REST api call
ob.OnNext(2);
await Task.Delay(1000);
ob.OnNext(3);
ob.OnCompleted();
});
observable.Subscribe(i => TestContext.WriteLine($"Sync {i}"));
observable.SelectMany(i => WriteAsync(i).ToObservable()).Subscribe();
await observable;
TestContext.WriteLine("Complete.");
}
public async Task WriteAsync(int value) // Fake async DB call
{
await Task.Delay(1000);
TestContext.WriteLine($"Async {value}");
}
修改 我意识到提到单元测试可能会产生误导。这不是一个测试问题。该代码模拟了Azure WebJob中运行的进程的实际问题,其中生产者和使用者都需要调用某些Async IO。问题是webjob在消费者真正完成之前就已经完成了。这是因为我无法弄清楚如何正确等待消费者方面的任何事情。也许这对RX来说是不可能的......
答案 0 :(得分:1)
修改强>:
您基本上正在寻找阻止运营商。旧的阻塞运算符(如DECLARE @Dt NVARCHAR(255)
SET @Dt = '2016-04-25T00:00:00'
SELECT CONVERT(NVARCHAR(10), CONVERT(DATE, CAST(FLOOR(CAST(CONVERT(DATETIME, @Dt) AS FLOAT)) AS DATETIME), 103), 103)
)已弃用,以支持异步版本。你想等待最后一项:
ForEach
虽然这可以解决您的直接问题,但由于以下情况,您似乎仍会继续遇到问题(我还增加了两个)。当使用正确时,Rx非常强大,而当没有使用时,它会让人感到困惑。
旧答案:
一些事情:
public async Task TestMethod1()
{
TestContext.WriteLine("Starting test...");
var observable = Observable.Create<int>(async ob =>
{
ob.OnNext(1);
await Task.Delay(1000);
ob.OnNext(2);
await Task.Delay(1000);
ob.OnNext(3);
ob.OnCompleted();
});
observable.Subscribe(i => TestContext.WriteLine($"Sync {i}"));
var selectManyObservable = observable.SelectMany(i => WriteAsync(i).ToObservable()).Publish().RefCount();
selectManyObservable.Subscribe();
await selectManyObservable.LastOrDefaultAsync();
TestContext.WriteLine("Complete.");
}
)最好仅在订阅中执行,而不是在WriteLine
等运算符中执行。它没有运行完成的原因是因为你的测试运行器。您的测试运行员在SelectMany
结束时终止测试。否则,Rx订阅将继续存在。当我在Linqpad中运行代码时,我得到以下输出:
开始测试...
同步1
同步2
异步1
同步3
异步2
完整。
Async 3
...这是我假设您想要看到的内容,除非您可能希望在Async 3之后完成。
仅使用Rx,您的代码看起来像这样:
TestMethod1
这仍然没有利用Rx的测试功能。这看起来像这样:
public void TestMethod1()
{
TestContext.WriteLine("Starting test...");
var observable = Observable.Concat<int>(
Observable.Return(1),
Observable.Empty<int>().Delay(TimeSpan.FromSeconds(1)),
Observable.Return(2),
Observable.Empty<int>().Delay(TimeSpan.FromSeconds(1)),
Observable.Return(3)
);
var syncOutput = observable
.Select(i => $"Sync {i}");
syncOutput.Subscribe(s => TestContext.WriteLine(s));
var asyncOutput = observable
.SelectMany(i => WriteAsync(i, scheduler));
asyncOutput.Subscribe(s => TestContext.WriteLine(s), () => TestContext.WriteLine("Complete."));
}
public IObservable<string> WriteAsync(int value, IScheduler scheduler)
{
return Observable.Return(value)
.Delay(TimeSpan.FromSeconds(1), scheduler)
.Select(i => $"Async {value}");
}
public static class TestContext
{
public static void WriteLine(string s)
{
Console.WriteLine(s);
}
}
...因此,与您的任务测试不同,您不必等待。测试立即执行。您可以将延迟时间提高到几分钟或几小时,而public void TestMethod1()
{
var scheduler = new TestScheduler();
TestContext.WriteLine("Starting test...");
var observable = Observable.Concat<int>(
Observable.Return(1),
Observable.Empty<int>().Delay(TimeSpan.FromSeconds(1), scheduler),
Observable.Return(2),
Observable.Empty<int>().Delay(TimeSpan.FromSeconds(1), scheduler),
Observable.Return(3)
);
var syncOutput = observable
.Select(i => $"Sync {i}");
syncOutput.Subscribe(s => TestContext.WriteLine(s));
var asyncOutput = observable
.SelectMany(i => WriteAsync(i, scheduler));
asyncOutput.Subscribe(s => TestContext.WriteLine(s), () => TestContext.WriteLine("Complete."));
var asyncExpected = scheduler.CreateColdObservable<string>(
ReactiveTest.OnNext(1000.Ms(), "Async 1"),
ReactiveTest.OnNext(2000.Ms(), "Async 2"),
ReactiveTest.OnNext(3000.Ms(), "Async 3"),
ReactiveTest.OnCompleted<string>(3000.Ms() + 1) //+1 because you can't have two notifications on same tick
);
var syncExpected = scheduler.CreateColdObservable<string>(
ReactiveTest.OnNext(0000.Ms(), "Sync 1"),
ReactiveTest.OnNext(1000.Ms(), "Sync 2"),
ReactiveTest.OnNext(2000.Ms(), "Sync 3"),
ReactiveTest.OnCompleted<string>(2000.Ms()) //why no +1 here?
);
var asyncObserver = scheduler.CreateObserver<string>();
asyncOutput.Subscribe(asyncObserver);
var syncObserver = scheduler.CreateObserver<string>();
syncOutput.Subscribe(syncObserver);
scheduler.Start();
ReactiveAssert.AreElementsEqual(
asyncExpected.Messages,
asyncObserver.Messages);
ReactiveAssert.AreElementsEqual(
syncExpected.Messages,
syncObserver.Messages);
}
public static class MyExtensions
{
public static long Ms(this int ms)
{
return TimeSpan.FromMilliseconds(ms).Ticks;
}
}
基本上会模拟您的时间。然后你的测试运动员可能会很高兴。
答案 1 :(得分:-1)
好吧,您可以使用Observable.ForEach
阻止,直到.png
终止:
IObservable
您可以使observable.ForEach(unusedValue => { });
成为普通的非异步方法,然后用此替换TestMethod1
吗?