ReactiveUI: Testing observable properties from uni

2019-08-17 19:16发布

There is this example on the official RX blog:

var scheduler = new TestScheduler();

var xs = scheduler.CreateColdObservable(
    OnNext(10, 42),
    OnCompleted<int>(20)
);

var res = scheduler.Start(() => xs);

res.Messages.AssertEqual(
    OnNext(210, 42),            // Subscribed + 10
    OnCompleted<int>(220)       // Subscribed + 20
);

xs.Subscriptions.AssertEqual(
    Subscribe(200, 1000)        // [Subscribed, Disposed]
);

I'd like to do something like this with reactiveui. I mean instead of the scheduler.CreateColdObservable(...) use the streams from actual property change notification. The problem is that I tried vm.ObservableForProperty and vm.Changed but they worked inconsistently (not all property change created an event or the value was null)

Here is the code of my VM:

internal class ProductFileEditorVM : ReactiveObject
{
    private readonly List<string> _preloadedList;

    private bool _OnlyContainingProduct;
    public bool OnlyContainingProduct
    {
        get { return _OnlyContainingProduct; }
        set
        {
            this.RaiseAndSetIfChanged(x => x.OnlyContainingProduct, value);
        }
    }

    private ObservableAsPropertyHelper<IEnumerable<string>> _RepoList;
    public IEnumerable<string> RepoList
    {
        get{return _RepoList.Value;}
    }

    public ProductFileEditorVM(RepositoryManager repositoryManager)
    {

        //Set defaults
        OnlyContainingProduct = true;

        //Preload
        _preloadedList = repositoryManager.GetList();

        var list = this.WhenAny(x => x.OnlyContainingProduct,
                     ocp =>
                     ocp.Value
                         ? _preloadedRepoList.Where(repo => repo.ToLower().Contains("product"))
                         : _preloadedRepoList);
        list.ToProperty(this, x => x.RepoList);
    }
}

Ideally I'd like to use Observable.CombineLatest on the two property and creating a tuple and comparing this tuple in the assert expression like in the first example.

The good result would be:

  1. [OnlyContainingProduct==true;RepoList= the filtered one]
  2. !change OnlyContainingProduct to false
  3. [OnlyContainingProduct==false;RepoList= the whole list]

*Or is this the wrong way to approach it? The only example I saw about this uses actual time measures like milliseconds but I don't see how they are useful except in case of Throttle and similar methods. *

1条回答
做个烂人
2楼-- · 2019-08-17 19:55

So, because you're not doing tests that are related to time, only tests that are based on order (i.e. "I did This, then I did This, then it should be That), it's actually far simpler to just write a normal unit test. TestScheduler is a big hammer :)

So, you could do something like:

var fixture = new ProductFileEditorVM();

bool repoListChanged = false;
fixture.WhenAny(x => x.RepoList, x => x.Value)
    .Subscribe(_ => repoListChanged = true);

fixture.OnlyContainingProduct = true;
Assert.True(repoListChanged);

When to use TestScheduler

However, if loading RepoList was asynchronous and could take some time, and you wanted to represent a "Loading" state, a TestScheduler would be good for that - you'd click the checkbox at say +20ms, AdvanceTo(200ms), check to see if you're in Loading state, AdvanceTo(10min), then see that the list is updated and the state isn't Loading

查看更多
登录 后发表回答