0

我在 MVVM 项目中使用 Rx 和 RxUI,并且有一个视图模型可以从 WCF 服务异步查询其数据。在单元测试中,我创建了一个模拟对象,它返回一个具有预期值的任务。

这是我的视图模型的大致概念

public class ViewModel : ReactiveObject
{
    private IContext _context;

    public ViewModel(IContext context)
    {
        _context = context;

        Observable.FromAsync(() => _context.WcfServiceCall())
              .Subscribe(result => {
                   Children.AddRange(results.Select(r => new ChildViewModel(r).ToList()));
               });
     }

     public ObservableCollection<ChildViewModel> { get; private set;}
}

我的单元测试看起来像这样

[TestFixture]
public class ViewModelTest : AssertionHelper
{
    [Test]
    public void ShouldSetChildren()
    {
        var c = new Mock<IContext>();
        c.Setup(q => q.WcfServiceCall())
            .Returns(Task.Run(() => new []{ 1,2,3,4,5,6 })):

        var vm = new ViewModel(c.Object);
        var p = vm.Children.First(); // this call sometimes fails

        ... 
     }
}

我遇到的问题是,我有超过 400 个测试可以做这种事情,而且它们大部分时间都可以工作,但是我随机得到失败的测试,一次一两个,报告序列没有值。这是不可预测和随机的。我可以在失败后再次运行测试并且全部成功。我已按照此处所述添加了 TestScheduler,但问题仍然存在。

有没有更好的方法来测试进行异步方法调用的方法?

根据 Paul Bett 的输入进行编辑:我看到 FromAsync 不采用 IScheduler 参数,但我确实有可用的 SubscribeOn 和 ObserveOn。

或者,我可以直接调用 WCF 异步方法并将返回的 Task 转换为 observable。我不确定我是否理解何时使用 Observable.FromAsync 与不使用它更合适。

4

2 回答 2

1

是否Observable.FromAsyncIScheduler参数?它是您的测试调度程序吗?

于 2013-01-15T23:45:37.790 回答
0

我意识到我的单元测试是不正确的,因为它们接触了太多的移动部件。我有一些测试可以验证模拟的 Web 服务调用是否按预期进行,我真的不应该在不关注该测试点的测试中包含这种复杂性。

导航到视图模型时,我正在调用 wcf 服务。这是我的视图模型的更好表示,在指定 IScheduler 方面稍作更改:

public class ViewModel : ReactiveObject, IRoutableViewModel
{
    private IContext _context;
    public ViewModel(IContext context)
    {
        _context = context;
        Weeks = new ReactiveCollection<WeekViewModel>();

        this.WhenNavigatedTo(() =>
        {
            _context.Service.GetWeeksAsync()
                .ToObservable()
                .ObserveOn(RxApp.DeferredScheduler)
                .Subscribe(result =>
                {
                    Weeks.AddRange(result.Select(w => WeekViewModel(w)));
                });
        });

        // ...
    }

    public ReactiveCollection<WeekViewModel> Weeks { get; private set; }
}

我现在不是设置上下文来填充 Weeks 集合,然后使用路由器导航到 ViewModel,而是在单元测试中将对象添加到 Weeks 集合并跳过导航和异步 Web 服务调用。据我所知,这减少了测试,消除了不稳定性,并减少了测试套件的执行时间。

所以我的单元测试看起来像这样

[TestFixture]
public class ViewModelTest : AssertionHelper
{
    [Test]
    public void ShouldSetChildren()
    {
        var c = new Mock<IContext>();
        var vm = new ViewModel(c.Object);
        vm.Children.AddRange((new []{1,2,3,4,5,6}).Select(i => new ChildViewModel(i)));

        var p = vm.Children.First(); // this is valid now - no timing issues

        ... 
    }
}

该代码在应用程序中运行正确,但在测试运行程序中存在问题,因此我相信这可以解决我的直接问题。

于 2013-01-16T19:11:40.493 回答