ReactiveUI - 在使用油门时测试订阅
ReactiveUI - testing subscribe when using throttle
我在尝试对使用 WhenAnyValue 侦听 属性 中的更改的视图模型进行单元测试时遇到问题,并根据新的填充列表属性 的值。由于与第 3 方软件交互,我需要使用 Throttle 来监听 属性 上的变化。
我的解决方案在生产中有效,但我的单元测试遇到了一些问题。好像跟没有让test scheduler正确推进有关,以至于throttle之后的subscription居然是运行。我已经创建了我的问题的简化版本,希望它能说明问题。
查看要测试的模型
public class ViewModel : ReactiveObject
{
public ViewModel(IScheduler scheduler)
{
ListToBeFilled = new List<int>();
this.WhenAnyValue(vm => vm.SelectedValue)
.Throttle(TimeSpan.FromMilliseconds(500), scheduler)
.Skip(1)
.Subscribe(value =>
{
// Do a computation based on value and store result in
// ListToBeFilled.
ListToBeFilled = new List<int>() {1, 2, 3};
});
}
private string _selectedValue;
public string SelectedValue
{
get { return _selectedValue; }
set { this.RaiseAndSetIfChanged(ref _selectedValue, value); }
}
public List<int> ListToBeFilled { get; set; }
}
查看模型测试
[TestFixture]
[RequiresSTA]
public class ViewModelTests
{
[Test]
public void TestViewModel()
{
// Arrange
(new TestScheduler()).With(scheduler =>
{
ViewModel vm = new ViewModel(scheduler);
// Act
vm.SelectedValue = "test value";
scheduler.AdvanceByMs(1000);
// Assert
Assert.AreEqual(3, vm.ListToBeFilled.Count);
});
}
}
测试失败说 预期:3 但为 0。当 运行 在不使用 Throttle 的情况下进行测试(我需要在生产中使用它)时,测试通过。我使用的测试调度程序错了吗?我需要做什么才能消耗 Throttle?
您的代码(几乎)完全正确,但您被 WhenAnyValue/Skip 行为欺骗了。
WhenAnyValue 将发布您尝试使用 Skip
跳过的初始值(空值)。但是因为 Throttle
在 TestScheduler 上是 运行,所以在有效启动调度程序 (AdvanceBy
) 之前不会触发 Skip,此时 2 个更改已排队(null 和 "test value"
), 但 Throttle
将删除第一个,Skip
第二个,因此您的订阅代码将永远不会被调用。
多种解决方法:
在设置测试值之前添加scheduler.AdvanceByMs(1000);
(将确保传播初始值)
将 Skip
移到 Throttle
之前(不需要调度程序,因此会立即应用)
(越晚越好,因为您的代码错误地假设它不会在视图模型创建后的 500 毫秒内获得选择,这不太可能,但也不是完全不可能)
我在尝试对使用 WhenAnyValue 侦听 属性 中的更改的视图模型进行单元测试时遇到问题,并根据新的填充列表属性 的值。由于与第 3 方软件交互,我需要使用 Throttle 来监听 属性 上的变化。
我的解决方案在生产中有效,但我的单元测试遇到了一些问题。好像跟没有让test scheduler正确推进有关,以至于throttle之后的subscription居然是运行。我已经创建了我的问题的简化版本,希望它能说明问题。
查看要测试的模型
public class ViewModel : ReactiveObject
{
public ViewModel(IScheduler scheduler)
{
ListToBeFilled = new List<int>();
this.WhenAnyValue(vm => vm.SelectedValue)
.Throttle(TimeSpan.FromMilliseconds(500), scheduler)
.Skip(1)
.Subscribe(value =>
{
// Do a computation based on value and store result in
// ListToBeFilled.
ListToBeFilled = new List<int>() {1, 2, 3};
});
}
private string _selectedValue;
public string SelectedValue
{
get { return _selectedValue; }
set { this.RaiseAndSetIfChanged(ref _selectedValue, value); }
}
public List<int> ListToBeFilled { get; set; }
}
查看模型测试
[TestFixture]
[RequiresSTA]
public class ViewModelTests
{
[Test]
public void TestViewModel()
{
// Arrange
(new TestScheduler()).With(scheduler =>
{
ViewModel vm = new ViewModel(scheduler);
// Act
vm.SelectedValue = "test value";
scheduler.AdvanceByMs(1000);
// Assert
Assert.AreEqual(3, vm.ListToBeFilled.Count);
});
}
}
测试失败说 预期:3 但为 0。当 运行 在不使用 Throttle 的情况下进行测试(我需要在生产中使用它)时,测试通过。我使用的测试调度程序错了吗?我需要做什么才能消耗 Throttle?
您的代码(几乎)完全正确,但您被 WhenAnyValue/Skip 行为欺骗了。
WhenAnyValue 将发布您尝试使用 Skip
跳过的初始值(空值)。但是因为 Throttle
在 TestScheduler 上是 运行,所以在有效启动调度程序 (AdvanceBy
) 之前不会触发 Skip,此时 2 个更改已排队(null 和 "test value"
), 但 Throttle
将删除第一个,Skip
第二个,因此您的订阅代码将永远不会被调用。
多种解决方法:
在设置测试值之前添加
scheduler.AdvanceByMs(1000);
(将确保传播初始值)将
Skip
移到Throttle
之前(不需要调度程序,因此会立即应用)
(越晚越好,因为您的代码错误地假设它不会在视图模型创建后的 500 毫秒内获得选择,这不太可能,但也不是完全不可能)