Answered Using TestScheduler to negate a throttle

  • Thursday, November 15, 2012 4:18 PM
     
      Has Code

    Hello,

    I have run into an issue using the testscheduler and a throttled observable on a viewmodel. 

    I have a class that sets up a throttled subscriber. Something like this:

    _throttledSubscriber = Observable.FromEventPattern<PropertyChangedEventArgs>(myObject, "PropertyChanged", InjectedScheduler)

                            .Where(t => t.EventArgs.PropertyName == "MyProperty")

                            .Throttle(TimeSpan.FromMilliseconds(1000), InjectedScheduler)

                            .Subscribe((e) => { Do Work here });

    I am trying to negate the throttle in a unit test using the InjectedScheduler (Set to TestScheduler). My Test:

    var testScheduler = new TestScheduler();

    viewModel.ReactiveScheduler = testScheduler;

    viewModel.MyProperty= "asdf"; //trigger event for observable

    //Trying here to manipulate the test scheduler to avoid throttling

    testScheduler.Start();

    testScheduler.AdvanceBy(5000);

    //this assert fails unless I do a Thread.Sleep

    Assert.IsTrue(Something changed);

    I have debugged the test scheduler and verified that it had the event in the queue. I cant figure out how to get that event processed and bypass the throttle. Start and AdvanceBy don't seem to schedule the event. Throwing in Thread.Sleep(1200) allows the real test to pass.

    Any thoughts?

    Thanks,

    Jeff

    Edit: I have derived my test clas from ReactiveTest and it didn't help.
    • Edited by jeffras Thursday, November 15, 2012 4:24 PM
    •  

All Replies

  • Friday, November 16, 2012 10:05 AM
     
     Answered Has Code

    A colleague and I both saw your example and had the same thoughts. Maybe you have dumbed-down the code for the forums, but in doing so you may have confused the issue. However we both noted that:

    1) You use a member called InjectedScheduler in your code under test, however you assign (not inject) a scheduler to a property called ReactiveScheduler

    2) It appears that you set up your throttled property change notification in the constructor of you ViewModel, however you assign the scheduler after construction. This means you will be using a different instance!

    I have included a full working sample of what you are trying to do. *Using NUnit. Swap in MSTest or what ever if you need, it has no affect on the concepts.

    using System;
    using System.ComponentModel;
    using System.Reactive.Concurrency;
    using System.Reactive.Linq;
    using Microsoft.Reactive.Testing;
    using NUnit.Framework;
    
    namespace RxForums.jeffras
    {
        [TestFixture]
        public sealed class TestingThrottleOnPropChange
        {
            [Test]
            public void Should_throttle_changes_to_count_prop()
            {
                var throttleScheduler = new TestScheduler();
                var vm = new ViewModel(throttleScheduler);
    
                vm.MyProperty = "asdf";
    
                //Prove that the thing has not been invoked immediately
                Assert.AreEqual(0, vm.MyPropertyChangedCount);
    
                throttleScheduler.AdvanceBy(TimeSpan.FromMilliseconds(1000).Ticks);
                Assert.AreEqual(1, vm.MyPropertyChangedCount);
            }
    
        }
    
        public class ViewModel : INotifyPropertyChanged, IDisposable
        {
            private readonly IDisposable _throttledSubscriber;
            private string _myProperty;
            
            public ViewModel(IScheduler throttleScheduler)
            {
                _throttledSubscriber = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>
                                    (h => (s, e) => h(s, e),
                                     h => PropertyChanged += h,
                                     h => PropertyChanged -= h)
                            .Where(t => t.EventArgs.PropertyName == "MyProperty")
                            .Throttle(TimeSpan.FromMilliseconds(1000), throttleScheduler)
                            .Subscribe(_ => { MyPropertyChangedCount++; });
            }
    
            public int MyPropertyChangedCount { get; private set; }
    
            public string MyProperty
            {
                get { return _myProperty; }
                set
                {
                    if (_myProperty != value)
                    {
                        _myProperty = value;
                        OnPropertyChanged("MyProperty");
                    }
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                var handler = PropertyChanged;
                if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
            }
    
            public void Dispose()
            {
                _throttledSubscriber.Dispose();
            }
        }
    }
    

    If this does not help, then please post an unedited version of the code that re-produces the issue and then we can help :-)

    Regards

    Lee (and Marcus)


    Lee Campbell http://LeeCampbell.blogspot.com

    • Marked As Answer by jeffras Friday, November 16, 2012 6:13 PM
    •  
  • Friday, November 16, 2012 6:16 PM
     
     

    Lee, you guys rock!

    Thanks for the example. I realized that I made a stupid mistake. In my test I am using AdvanceBy(1000) where I should be using AdvanceBy(TimeSpan.FromMilliseconds(1000).Ticks)

    I owe you and your colleague a pitcher!