1
votes

I have a ReactiveAsyncCommand that for the moment are just sleeping for a while:

public ReactiveAsyncCommand SignIn { get; private set; }

//from constructor:
SignIn = new ReactiveAsyncCommand(this.WhenAny(x => x.Username, x => x.Password,
                                                  (u, p) =>
                                                  !string.IsNullOrWhiteSpace(u.Value) &&
                                                  !string.IsNullOrWhiteSpace(p.Value)));

SignIn.RegisterAsyncAction(_ => Thread.Sleep(4000));

I want to show a progress indicator while the command executes, so I have made a property to bind its visibility against:

private ObservableAsPropertyHelper<bool> _Waiting; 
public bool Waiting
{
    get { return _Waiting.Value; }
}

//from constructor:
_Waiting = SignIn.ItemsInflight
                 .Select(x => x > 0)
                 .ToProperty(this, x => x.Waiting);

So, even though it seems to work in practice, I would love a unit test showing that Waiting allways will be true while the command executes, and only then.

I have read this blogpost about the testscheduler, but struggle to put it to use.

    [TestMethod]
    public void flag_waiting_while_signing_in()
    {

        (new TestScheduler()).With(scheduler =>
            {
                var vm = new SignInViewModel {Username = "username", Password = "password"};

                vm.SignIn.Execute(null);

                Assert.IsTrue(vm.Waiting); 
            });
    }

This test fails (waiting is false). I have tried to add a calls to scheduler.start() and scheduler.advanceBy( ) but that didn't make any difference.

Is my approach to testing this wrong? If the approach is right, what else is wrong?

Edit
So I changed the Thread.Sleep() as suggested:

SignIn.RegisterAsyncAction(_ =>
            {
                Observable.Interval(TimeSpan.FromMilliseconds(4000));
            });

And tried to control time by calling scheduler.AdvanceBy(...) before checking the Waiting-flag. Still no green, though.

1
You need to use RegisterAsyncObservable instead, and specify RxApp.DispatcherScheduler as the scheduler for Interval - Ana Betts
I'm not sure I understand. First, by RxApp.DispatcherScheduler, do you mean RxApp.DeferredScheduler? Second, in the comment above DeferredScheduler, it says that this scheduler is used for work items that should run on the ui thread. Eventually, I want my signin command to do something more useful. It the idea that I use the deferredscheduler when unittesting and the taskpool scheduler for real work? Anyway, the test goes green now, but by checking that the flag is reset after the 4 sec delay, it went red again. Any more suggestions? - Vegar
Yeah, I meant DeferredScheduler, though you're right, it's more realistic to use TaskPoolScheduler (and you'll get the same result!) - Ana Betts

1 Answers

1
votes

The reason that TestScheduler is falling over, is that you have a source of asynchrony that is outside TestScheduler's view - the Thread.Sleep. There's no way that it can control it, it will always take up 4 real seconds. Replace it with an Observable.Interval instead and it should work as you expect

[TestMethod]
public void flag_waiting_while_signing_in()
{

    (new TestScheduler()).With(scheduler =>
        {
            var vm = new SignInViewModel {Username = "username", Password = "password"};

            vm.SignIn.Execute(null);

            scheduler.AdvanceBy(TimeSpan.FromMilliseconds(2000));
            Assert.IsTrue(vm.Waiting); 

            // Move past the end
            scheduler.AdvanceBy(TimeSpan.FromMilliseconds(5000));
            Assert.IsFalse(vm.Waiting); 
        });
}