根据一些评论,我应该清楚地表明,这个问题是关于TestScheduler为什么抛出空引用异常,而不是如何使测试通过。一个较早的示例假定与TPL的交互是问题的原因,但我现在发现触发此行为不是必需的,因此我用一个更简单的测试用例替换了代码
我在尝试将rx测试“虚拟时间” TestScheduler与后台线程结合在一起时遇到一些问题。在文章的底部显示了我发现的最简单的方法来演示该问题。
该代码只是在运行一个后台线程,该线程订阅带有超时的可观察序列。
超时是由TestScheduler驱动的,当我在主线程上进行超时时,将生成空引用异常:
断言失败。虚拟时间00:00:00.1394720,异常System.NullReferenceException:对象引用未设置为对象的实例。在System.Reactive.Concurrency.VirtualTimeScheduler
2.GetNext() at System.Reactive.Concurrency.VirtualTimeSchedulerBase
2.AdvanceTo(TAbsolute time)处..... UnitTest1.cs:line
该故障似乎对所使用的IObservable的确切类型不敏感,但似乎取决于Timeout选择器的存在。但是有趣的是,测试通常会在虚拟时间失败,因为超时是在火灾引发的超时之前。
运行与控制台应用程序相同的代码也似乎可以正常工作,尽管该问题似乎很容易受到干扰(可能是竞争条件),因此这可能会引起麻烦。
进一步的研究和评论强烈暗示,该行为是由于在后台线程调度其超时操作而使调度程序前进时发生的竞争条件引起的
在此先感谢您提供的任何信息...
using Microsoft.Reactive.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Reactive.Linq;
using System.Threading;
namespace UnitTestProject1
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestBackgroundThreadWithTestScheduler()
{
var scheduler = new TestScheduler();
var seq = Observable.Never<string>();
bool subscribed = false;
ThreadPool.QueueUserWorkItem(_ =>
{
Thread.Sleep(100); //wait a bit to give main thread chance to start advancing scheduler
seq.Timeout(TimeSpan.FromSeconds(10), scheduler)
.Subscribe(s => {/*never called*/});
subscribed = true; //signal we're subscribed
});
//---- Uncommenting this line to avoid the race condition appears to fix the test ----
//while (!subscribed) Thread.Yield();
//Advance the scheduer in small increments to maximise our chances of hitting the race
var watch = scheduler.StartStopwatch();
try
{
while (watch.Elapsed < TimeSpan.FromSeconds(20)) scheduler.AdvanceBy(10);
}
//NullReference is thrown unexpectedly
catch (NullReferenceException ex)
{
Assert.Fail("Virtual time {0}, exception {1}", watch.Elapsed, ex);
}
catch (TimeoutException)
{
//desired result is a TimeoutException so this is a test pass
}
}
}
}
这条线在堆栈跟踪System.Reactive.Concurrency.VirtualTimeScheduler2.GetNext()
是一个线索,那就是错误确实是您访问从2个线程调度器( 该TestScheduler不是线程安全的 但目前的一个错误在TestScheduler
从多个线程访问它时)。
您的第一个评论可能是正确的:后台任务正在添加到调度程序中,就像您的其他任务正在推进调度程序一样。
尝试在访问调度程序的语句周围添加锁,看看是否可以解决您的问题。
当然,真正的解决方法是在设置好自己的后台任务信号后,让测试任务在继续之前先等待该信号。因为即使TestScheduler
没有错误,您的测试也有一个竞争条件,即测试线程可以在后台线程订阅可观察对象之前完成。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句