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