Unit testing ReactiveUI-取消另一个命令-文档中代码的单元测试失败

Unit testing ReactiveUI-取消另一个命令-文档中代码的单元测试失败,unit-testing,reactiveui,Unit Testing,Reactiveui,我从中获取代码并尝试对其进行单元测试,但失败。 它是关于调用一个命令来取消另一个命令 下面是要测试的类 public class SomeViewModel : ReactiveObject { public SomeViewModel(IScheduler scheduler) { this.CancelableCommand = ReactiveCommand .CreateFromObservable(

我从中获取代码并尝试对其进行单元测试,但失败。
它是关于调用一个命令来取消另一个命令

下面是要测试的类

public class SomeViewModel : ReactiveObject
{
    public SomeViewModel(IScheduler scheduler)
    {
        this.CancelableCommand = ReactiveCommand
            .CreateFromObservable(
                () => Observable
                    .StartAsync(DoSomethingAsync)
                    .TakeUntil(CancelCommand), outputScheduler: scheduler);
        this.CancelCommand = ReactiveCommand.Create(
            () =>
            {
                Debug.WriteLine("Cancelling");
            },
            this.CancelableCommand.IsExecuting, scheduler);
    }

    public ReactiveCommand<Unit, Unit> CancelableCommand
    {
        get;
        private set;
    }

    public ReactiveCommand<Unit, Unit> CancelCommand
    {
        get;
        private set;
    }

    public bool IsCancelled { get; private set; }

    private async Task DoSomethingAsync(CancellationToken ct)
    {
        try
        {
            await Task.Delay(TimeSpan.FromSeconds(3), ct);
        }
        catch (TaskCanceledException)
        {
            IsCancelled = true;
        }
    }
}
执行
cancel命令
(控制台日志上的断点被命中),但是
任务延迟
从未被取消。似乎,
TakeUntil
没有请求取消

更新
我编辑了上面的代码,以便我的
ViewModel
ctor使用
IScheduler
并根据创建命令,但测试仍然失败。
我试过nUnit和xUnit。
我还尝试按照在测试设置中执行
RxApp.MainThreadScheduler=Scheduler.Immediate
,但仍然失败

更新2
从我标记为答案和注释的解决方案来看,最简单的方法是不在ctor中使用isScheduler,然后像这样编写测试,测试就通过了

[Test]
    public async Task Executing_cancel_should_cancel_cancelableTask()
    {
        var sut = new SomeViewModel();
        sut.CancelableCommand.Execute().Subscribe();
        await sut.CancelCommand.Execute();

        Assert.IsTrue(sut.IsCancelled);
    }

这对我有用:

public class SomeViewModelTest
{
    SomeViewModel m_actual;

    [SetUp]
    public void Setup()
    {
        m_actual = new SomeViewModel(CurrentThreadScheduler.Instance); 
        m_actual.Activator.Activate();
    }

    [Test]
    public void Executing_cancel_should_cancel_cancelableTask()
    {
        m_actual.CancelableCommand.Execute().Subscribe();
        m_actual.CancelCommand.Execute().Subscribe();

        Assert.IsTrue(m_actual.IsCancelled);
    } 
}
我在测试中更改了调度程序以使用相同的方法,并且我的ViewModel确实实现了ISupportsActivation,我敢说在这里不会有任何区别。
除此之外,我从测试中删除了async/await,在Rx中不需要它,只需订阅命令。

问题是,取消命令后,您正在等待命令的执行。由于取消发生在命令执行之前,因此不会影响命令的执行。您可以按如下方式使其通过:

public async Task Executing_cancel_should_cancel_cancelableTask()
{
    var sut = new SomeViewModel(Scheduler.Immediate);
    sut.CancelableCommand.Execute().Subscribe();
    await sut.CancelCommand.Execute();

    Assert.True(sut.IsCancelled);
}
在这里,我立即开始执行命令(通过订阅)。随后的取消将影响该执行


一般来说,Rx和TPL的混合总是有点混乱。这是可行的,但每个角落都潜伏着这样的陷阱和丑恶。作为一个长期的解决方案,我强烈建议转向“纯”Rx。您不会回头看-这太神奇了。

也许,您需要看看如何使用没有延迟的单元测试运行程序。快速搜索发现这篇文章可能会有所帮助。Tks但是:i)我不完全理解ii)RxApp没有aDeferredScheduler属性ii)在文章中,作者说reactiveui正在检测测试运行程序并正在执行调度程序切换。DeferredScheduler被重命名为MainThreadScheduler。我使它成为即时调度程序,但测试仍然失败。我在用NUnit。将尝试使用xUnit。问题仍然存在于即时计划程序以及nUnit和xUnit上。@François如果这是一个测试/学习项目,您介意与我分享吗,这样我可以尝试调试并查看发生了什么?实际上,订阅使其工作(而不是安装或激活程序)。我不明白为什么。我会深入调查医生的。Tks无论如何:-)调度程序也是无用的。解决方案只来自于订阅。所以考虑一下这个问题。。。调用Execute时,它将调用传递到ReactiveCommand中的函数。因此,在您的例子中,该函数是Observable.StartAsync,用于检索任务,但此时,即使检索到任务,Observable本身仍然没有被订阅。因此,在您等待取消的原始代码中,“CancelableCommand”在技术上仍然没有启动。所以cancel命令运行,然后完成运行,然后您“等待t”,但现在cancel命令已经运行了。希望这能让SENSEYA变得有意义,但它是值得的:-)我只想指出,这更多的是关于理解Observable.StartAsync是如何工作的,而不是RxUI是如何工作的。当你调用Observable.StartAsync时,它将立即执行你的任务方法,但是Observable本身在技术上还没有启动。因此,如果您想延迟任务的执行,那么您需要将Observable.StartAsync与Observable.deference包在一起,这将是最困难的部分。我不仅对ReactiveUI而且对Rx编程都是新手。我非常习惯于任务,而不是观察到的任务。每次执行一个步骤,在取消任务后等待该任务,该步骤可处理任务。我会采纳你的建议,避免把任务和观察结果混为一谈
public async Task Executing_cancel_should_cancel_cancelableTask()
{
    var sut = new SomeViewModel(Scheduler.Immediate);
    sut.CancelableCommand.Execute().Subscribe();
    await sut.CancelCommand.Execute();

    Assert.True(sut.IsCancelled);
}