ObservableAsPropertyHelper - 必须访问Value才能让它订阅?

时间:2014-03-16 04:04:47

标签: reactiveui

我最近将我的ReactiveUI金块包更新到最新的预发行版本(v5.99.4-beta)。我做了更新已经有一段时间了,但是我对ViewModel的许多测试都失败了。

调试器似乎表明我正常的Rx序列没有被订阅。例如:

  _executeSearch = ReactiveCommand.Create();
  var paper = _executeSearch
              .Where(x => x is string)
              .SelectMany(x => _searchParser.GetPaperFinders(x as string))
              .SelectMany(x => x.FindPaper());
  paper
       .Select(x => x.Item1.Title)
       .ToProperty(this, x => x.Title, out _TitleOAPH, "");

我的测试代码看起来像这样(为了简洁起见):

  INavService obj = Mock...
  ISearchStringParser parser = ...

  var vm = new AddCDSPaperViewModel(obj, parser);
  vm.CDSLookupString = "1234"; // trigger the "search"
  ... (test scheduler is advanced to wait for search results to show up
  Assert.AreEqual("title", vm.Title, "searched for title"); // vm.Title is unset, though it should be!

我查看了github上的reactiveui源代码,并发现了以下ObservableAsPropertyHelper对象的“value”属性:

    public T Value {
        get { 
            _inner = _inner ?? _source.Connect();
            return _lastValue; 
        }
    }

关键行是“_source.Connect()” - 简而言之,在有人访问Value之前,序列不会被订阅。在我的测试中,如果我在运行任何rx序列之前输入“vm.Title”,那么一切正常。

这是令人惊讶的行为(至少对我来说),因为必须在捕获任何值之前访问ObservableAsPropertyHelper.Value属性。这是预期的行为吗?这是为了提高效率(例如懒惰实例化)的原因吗?如果是这样,在测试中绕过这个的正确方法是什么?

或者我对这是如何工作的错误? : - )

1 个答案:

答案 0 :(得分:1)

  

这是预期的行为吗?

是的,这是ReactiveUI 6.0的新功能。

  

是否为效率(例如懒惰的实例化)原因而做了?

正确 - 它实际上可以最终节省大量内存和工作! (我在早期的测试中回忆起GitHub for Windows中15-20%的内存)

这是一个有趣的场景 - 我怀疑在单元测试运行中,我们应该订阅构造,类似于RxUI 5.x所做的。

编辑:我会这样写:

_executeSearch = ReactiveCommand.Create(_ => Observable.Return(x)
    .Where(x => x is string)
    .SelectMany(x => _searchParser.GetPaperFinders(x as string))
    .SelectMany(x => x.FindPaper()));

_executeSearch
   .Select(x => x.Item1.Title)
   .ToProperty(this, x => x.Title, out _TitleOAPH, "");

现在,您可以更轻松地编写测试方式,无需TestScheduler:

var vm = new AddCDSPaperViewModel(obj, parser);
var result = await vm.ExecuteSearch.ExecuteAsync("Some String");
Assert.AreEqual("title", result.Title);