So, you've got a few things in your code that is stopping you from getting things to work correctly:
Don't invoke commands in ViewModel Constructors
First, calling Execute
in the constructor means you'll never see the state change. The best pattern is to write that command but not execute it in the VM immediately, then in the View:
this.WhenAnyValue(x => x.ViewModel)
.InvokeCommand(this, x => x.ViewModel.PerformSearchCommand);
Move the clock after async actions
Ok, now that we can properly test the before and after state, we have to realize that after every time we do something that normally would be async, we have to advance the scheduler if we use TestScheduler. This means, that when we invoke the command, we should immediately advance the clock:
Assert.IsTrue(vm.PerformSearchCommand.CanExecute(null));
vm.PerformSearchCommand.Execute(null);
sched.AdvanceByMs(10);
Can't test Time Travel without IObservable
However, the trick is, your mock executes code immediately, there's no delay, so you'll never see it be busy. It just returns a canned value. Unfortunately, injecting the Repository makes this difficult to test if you want to see IsBusy
toggle.
So, let's rig the constructor a little bit:
public ViewModel(IAdventureWorksRepository _awRepository, Func<IObservable<List<Customer>>> searchCommand = null)
{
PerformSearchCommand = new ReactiveCommand();
searchCommand = searchCommand ?? () => Observable.Start(() => {
return _awRepository.vIndividualCustomers.Take(1000).ToList();
}, RxApp.TaskPoolScheduler);
PerformSearchCommand.RegisterAsync(searchCommand)
.Subscribe(rval => {
CustomerList = rval;
SelectedCustomer = CustomerList.FirstOrDefault();
});
PerformSearchCommand.IsExecuting
.ToProperty(this, x => x.IsBusy, out _IsBusy);
}
Set up the test now
Now, we can set up the test, to replace PerformSearchCommand's action with something that has a delay on it:
new TestScheduler().With(sched =>
{
var repoMock = new Mock<IAdventureWorksRepository>();
var vm = new ViewModel(repoMock.Object, () =>
Observable.Return(new[] { new vIndividualCustomer(), })
.Delay(TimeSpan.FromSeconds(1.0), sched));
Assert.AreEqual(false, vm.IsBusy);
Assert.AreEqual(0, vm.CustomerList.Count);
vm.PerformSearchCommand.Execute(null);
sched.AdvanceByMs(10);
// We should be busy, we haven't finished yet - no customers
Assert.AreEqual(true, vm.IsBusy);
Assert.AreEqual(0, vm.CustomerList.Count);
// Skip ahead to after we've returned the customer
sched.AdvanceByMs(1000);
Assert.AreEqual(false, vm.IsBusy);
Assert.AreEqual(1, vm.CustomerList.Count);
});