By Peter Daukintis
I decided to explore the Service Locator pattern to implement handling of the busy indicator. This involves considering the busy indicator as a service which can be called upon by the view model. Again it is implemented behind an interface to support unit testing. The interface looks like this:
public interface IBusyIndicator { void Hide(object token); void Show(object token); }
I based this solution on this source http://www.orktane.com/Blog/post/2010/01/23/iPhone-Sudoku-in-Silverlight-using-MVVM.aspx although my implementation is slightly different. So, the idea is that I have a UserControl which implements the IBusyIndicator interface. The user control gets added into the visual tree via the xaml and is registered into a static service locator class. The view model can request the service interface and make calls into it. The runtime objects can be faked to support unit testing.
Here’s the very simple ServiceLocator:
public static class ServiceLocator { private static IBusyIndicator _busyIndicator; public static void SetBusyIndicatorService(IBusyIndicator busyIndicator) { _busyIndicator = busyIndicator; } public static IBusyIndicator BusyIndicatorServiceStatic { get { return _busyIndicator; } } }
With this new interface we can extend the Execute login command functionality. Calls to
ServiceLocator.BusyIndicatorServiceStatic.Show(_token);
and the corresponding hide can be made in the Execute flow. Now, this will cause NullReference exceptions in the Login command tests so we’d better go and fix these up. The following line can be inserted before the view model creation in each test method that will execute the login command
ServiceLocator.SetBusyIndicatorService(new FakeBusyIndicator());
where FakeBusyIndicator is defined as follows:
public class FakeBusyIndicator : IBusyIndicator { #region Implementation of IBusyIndicator public void Hide(object token) { } public void Show(object token) { } #endregion }
Also, the addition of a mock busy indicator:
public class MockBusyIndicator : IBusyIndicator { public int HideCount { get; private set; } public int ShowCount { get; private set; } #region Implementation of IBusyIndicator public void Hide(object token) { ++HideCount; } public void Show(object token) { ++ShowCount; } #endregion }
enables the following unit test to be implemented.
[TestMethod] [Asynchronous] public void LoginCommand_ExecuteWithValidParameters_CausesCorrectBusyIndicatorShowHide() { var fakeUserRepository = new FakeUserRepository(); var mockBusyIndicator = new MockBusyIndicator(); ServiceLocator.SetBusyIndicatorService(mockBusyIndicator); var mainViewModel = new MainViewModel(fakeUserRepository) { UsernameText = "user", PasswordText = "pwd" }; mainViewModel.LoginCommand.Execute(null); EnqueueConditional(() => mainViewModel.IsLoading == false); EnqueueCallback(() => Assert.IsTrue(mockBusyIndicator.HideCount > 0 && mockBusyIndicator.ShowCount > 0 && mockBusyIndicator.HideCount == mockBusyIndicator.ShowCount)); EnqueueTestComplete(); }
A quick run of the test project shows all tests pass!
So, that’s about it for the view model and the unit tests – next for the fun bit of creating the user interface and wiring up the data-binding of the view model.
Technorati Tags: MVVM,Part,Peter,Daukintis,Service,Locator,indicator,Again,interface,unit,IBusyIndicator,Hide,solution,Blog,Sudoku,implementation,UserControl,implements,user,tree,objects,Here,ServiceLocator,_busyIndicator,SetBusyIndicatorService,BusyIndicatorServiceStatic,Execute,_token,NullReference,Login,creation,method,FakeBusyIndicator,region,Also,addition,MockBusyIndicator,HideCount,ShowCount,TestMethod,Asynchronous,LoginCommand_ExecuteWithValidParameters_CausesCorrectBusyIndicatorShowHide,FakeUserRepository,MainViewModel,UsernameText,PasswordText,LoginCommand,EnqueueConditional,EnqueueCallback,Assert,IsTrue,EnqueueTestComplete,data,busyIndicator,endregion
Windows Live Tags: MVVM,Part,Peter,Daukintis,Service,Locator,indicator,Again,interface,unit,IBusyIndicator,Hide,solution,Blog,Sudoku,implementation,UserControl,implements,user,tree,objects,Here,ServiceLocator,_busyIndicator,SetBusyIndicatorService,BusyIndicatorServiceStatic,Execute,_token,NullReference,Login,creation,method,FakeBusyIndicator,region,Also,addition,MockBusyIndicator,HideCount,ShowCount,TestMethod,Asynchronous,LoginCommand_ExecuteWithValidParameters_CausesCorrectBusyIndicatorShowHide,FakeUserRepository,MainViewModel,UsernameText,PasswordText,LoginCommand,EnqueueConditional,EnqueueCallback,Assert,IsTrue,EnqueueTestComplete,data,busyIndicator,endregion