0

I use Xamarin and ReactiveUI to do mobile apps.

I'd like to test my ViewModel and its ReactiveCommand.

I register an asynchronous operation which return an IObservable inside the ReactiveCommand.

In ViewModel constructor :

Login = new ReactiveCommand();
var loginResult = Login.RegisterAsync(_ => Client.Login(Username, Password));
loginResult.ObserveOn(RxApp.MainThreadScheduler).BindTo(this, self => self.ConnectedUser);
Login
    .Where(_ => ConnectedUser != null)
    .Subscribe(_ => {
        ConnectedUser.Disconnect();
        ConnectedUser = null;
    });

What's in Client.Login :

return Observable.Start(async () => {
    // Do an HTTP POST to login the user, with await stuff in
    // Return the User on success or throw an exception on error
}, RxApp.TaskpoolScheduler)
.Select(task => task.Result);

How to test the ViewModel login success ? Here is my approach which doesn't work :

[Test]
public void TestLoginSuccess()
{
    ViewModel viewModel = new ViewModel();
    viewModel.Username = "toto";
    viewModel.Password = "****";

    viewModel.Login.Execute(null);

    Assert.That(viewModel.ConnectedUser, Is.Not.Null); // always null so fail :(
}

The test fails because the assertion is made before the command has finished executing.

Resolution

I was subscribing to the Login command and assumed that my "disconnection block" would be called before the RegisterAsync's one. Inverted the two would resolve the issue or simply put the "disconnection" logic inside RegisterAsync like this :

var loginResult = Login.RegisterAsync(_ => {
    if (ConnectedUser != null)
    {
        ConnectedUser.Disconnect();
        ConnectedUser = null;
    }
    Client.Login(Username, Password);
});

Paul Betts ' solution was also necessary.

Community
  • 1
  • 1
nverinaud
  • 1,270
  • 14
  • 25
  • Can you also add the log from your output window. Normally I'd expect to ser ReactiveUI telling you that its set its scheduling to immediate as testing was detected. – AlSki Oct 25 '13 at 12:05

1 Answers1

2

TaskPoolScheduler is not set to Scheduler.Immediate, so it really is running in the background. Try this:

Scheduler.CurrentThread.With(sched => {
    // TODO: Write your code here, now both schedulers are
    // rigged to CurrentThread.
});
Ana Betts
  • 73,868
  • 16
  • 141
  • 209
  • It seems to correctly sequence the work (I checked using some breakpoints and they are triggered in correct order) but the binding to ConnectedUser is not made before the assertion. ConnectedUser remains null after the Login command has executed. – nverinaud Oct 28 '13 at 15:49