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

时间:2017-02-17 17:12:40

标签: unit-testing reactiveui

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

以下是要测试的课程。

private void openForm()
{
    if (this.WindowState == FormWindowState.Minimized)
    {
        this.WindowState = FormWindowState.Normal;
    }
    this.Show();
}

private void closeForm(string text)
{
    if (text != "")
    {
        notifyIcon.BalloonTipText = text;
        notifyIcon.ShowBalloonTip(30);
    }
    this.Hide();
}

private void Main_Resize(object sender, EventArgs e)
{
    if (WindowState == FormWindowState.Minimized)
    {
        this.closeForm("Click here to access Spotify Extender again!");
    }
}

private void Main_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == System.Windows.Forms.CloseReason.UserClosing)
    {
        e.Cancel = true;
        this.closeForm("Click here to access Spotify Extender again!");
    }
}

private void notifyIcon_MouseUp(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
        this.openForm();
}

这是单元测试:

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

[TestFixture] [Category("ViewModels")] public class SomeViewModelFixture { public async Task Executing_cancel_should_cancel_cancelableTask() { var sut = new SomeViewModel(Scheduler.Immediate); var t = sut.CancelableCommand.Execute(); await sut.CancelCommand.Execute(); await t; Assert.IsTrue(sut.IsCancelled); } } 已被执行(控制台日志上的断点被点击)但是CancelCommand永远不会被取消。似乎Task.Delay没有要求取消。

更新
我编辑了上面的代码,以便我的TakeUntil ctor采用ViewModel并根据that issue about unit testing创建命令,但测试仍然失败。
我尝试了nUnit和xUnit 我还尝试按照that article在我的测试设置中执行IScheduler,但仍然失败。

UPDATE2
从我标记为答案和注释的解决方案中,最简单的是不要在ctor中使用IScheduler,然后像这样编写测试并通过。

RxApp.MainThreadScheduler = Scheduler.Immediate

2 个答案:

答案 0 :(得分:3)

这对我有用:

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,只是订阅了命令。

答案 1 :(得分:2)

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

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。你不会回头 - 这太棒了。