In the 4th part of NGTweet series I had discussed about plans to introduce new functionality to NGTweet. But I wasn’t feeling great about the way the application was developed. I wasn’t following TDD, the application did not have much separation of concerns, the code looked very spaghetti.I somehow don’t feel comfortable working with spaghetti code.
I decided to refactor the work done so far. Initially I was thinking of introducing one change at a time. But then it would have been boring to add one feature and blog about it. So I have done a big bang refactoring and this post is about the summary of changes done since the last post. While refactoring I did come across some hiccups. I’ll try to highlight them as well.
Refactoring 1 – Unit test service
Easiest thing to start off was to add Unit tests for the TweetSharp service. There is minimal logic in the service as it acts as a wrapper over the TweetSharp library. But still I wanted to make sure that the features that I am going to add, do not break the existing functionality. I also wanted to try my hands at mocking framework called Moq. I have been using RhinoMocks for past 18 months or so. Many people suggest that Moq is easier, elegant and better to use. This was a easier thing to do. Both RhinoMocks and Moq have similar concepts and I am sure it must be same for any dynamic mocking framework available out there. So the first step I did was to unit test the service. These are straightforward tests and I won’t replicate them here. You can check them out in the source code available for download.
Refactoring 2 - Abstract IsolatedStorage into an Interface
The user interface layer had one of the most unstructured code I had written ever. There were direct calls to service, the presentation and business logic was residing in the same file. External dependencies like IsolatedStorage were directly used without being abstracted. Many people would say that using IsolatedStorage directly in code is acceptable as it is part of Silverlight. But I feel a strong urge to provide a level of indirection by abstracting it into an interface.
There are various reasons for doing so. First and the primary reason is it allows us to unit test the application. But that’s not a strong reason. People who don’t follow TDD will argue that there are applications which are using IsolatedStorage and not doing TDD which are running fine in production environment.
The other reason is to avoid code changes due to changes to the underlying framework. I’ll take an example here. We can use configuration settings by means of Web.config or App.config depending on the type of application. We can programmatically access these settings. This functionality is available since the beginning of DotNet framework. But the way in which these settings are accessed has undergone multiple changes with almost every major release of Dotnet framework. It could be in the form of introduction of ConfigurationManager class or something else liek deprecating some methods. Imagine you are accessing these settings in 100 different files. When the framework changes and you decide to upgrade your codebase you’ll have to change in all those 100 places. Instead if we abstract it to a centralised location, maintenance of such code wouldn’t be a problem at all.
Based on whatever I was doing to work with IsolatedStorage I created an interface called IApplicationSettingsProvider
public interface IApplicationSettingsProvider
{
object this[string key]
{
get;
set;
}
bool Contains(string key);
}
Another advantage I have here is I can swap the implementation of IApplicationSettingsProvider. For a Silverlight application I can use IsolatedStorage. But for a WPF application I can use some other storage mechanism like an encrypted XML file or a database or an object database. For an ASP.NET or ASP.NET MVC application I can use build in provider model to store application settings. The possibilities are many because we’ll be programming to an interface instead of a concrete class.
Thais step ensured that one of the infrastructure dependency was nicely abstracted.
Refactoring 3 - Integrate MVVMLight
Since the introduction of NuGet integrating third party dll’s is become a piece of cake. I did it for Moq and this time around for MVVM Light. I am using Moq again in the unit tests related to ViewModel. One thing I did not appreciate from the previous code was that the view had too much knowledge about the model. It was aware of which service was getting called, what was the internal data structure etc.
Ideally a view should only be concerned about displaying data to the user and handling user actions like selecting of a drop down list or text input or button click etc. But in our case the view was aware that the item source to which the list was bound to had a sub property called User.ScreenName. To me this looks like violation of the basic rule that view should be as dumb as possible.
So to overcome this we introduce our good friend, view model from the Model View ViewModel presentation pattern. So I refactored the main page to have a MainViewModel as its view model. This view model would be responsible for performing the service operations and updating the UI. The individual items in the listbox are also having their own view model which is called TweeterStatusViewModel. I’ll talk about this in much more detail in the future post.
I had a temptation to use the DialogService for showing the message boxes. In fact I did have one and it was working fine. But then I cam across a nice feature in MVVM Light which allows you to create messages in a very decoupled way using the DialogMessage class. There is also a feature called Messenger which acts more like an EventAggregator of Prism. Using Messenger we can publish messages from the view model and subscribe them from other view models or views itself. So I made use of this to display message box instead of taking the dialog service route. I am still not sure what is the best approach. May be as the application grows I’ll get a better picture if I need to provide an abstraction over the Messenger itself. But for time being I’ll not be using the dialog service.
Another thing that needs special attention that needs to be paid while updating the view model properties from the Async calls made to the web service. An exception is encountered while trying to update properties bound to view on the view model. In SIlverlight the service calls are always made asynchronously. As a result the callback runs on a separate thread. When we try to update view model properties, we get cross thread access exception. To overcome this we can use the Dispatcher class available in Silverlight or WPF.
Once again MVVMLight comes to the rescue. It has a nice little helper called DispatcherHelper to help us run code on the UI thread. For the DispatcherHelper to work we need to initialize it when the application starts. I added a line in the Application_Startup event as follows
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new MainPage();
DispatcherHelper.Initialize();
}
After the DispatcherHelper is intialized, it can be used from anywhere like
public bool HasTweets
{
get
{
return _hasTweets;
}
set
{
if (_hasTweets != value)
{
_hasTweets = value;
DispatcherHelper.UIDispatcher.BeginInvoke(() => RaisePropertyChanged("HasTweets"));
}
}
}
Refactoring 4 – Deploy web application to IIS Server
After I converted the application to view model, the images were not visible in the displayed results. There seems to be a limitation with Silverlight that you cannot access URL’s for images and media if you are running from a file based location which is the default if you are using the built in web server of Visual Studio 2010. You’ll encounter the AG_E_NETWORK_ERROR as shown below
The Silverlight Image control raises an ImageFailed event whenever it is unable to bind the image to the image source. We can tap into this event and debug based on the type of the exception. In my case it was a network error. So I changed the project setting to deploy the application to local IIS web server.
With this change all the images were displayed as before. Please note that this change of hosting the application in IIS needs to done for the Web application project which is used for hosting the Silverlight application.
Refactoring 5 – Unit Test View Model
To unit test the ViewModel I created a Silverlight Unit Test Application. There was some problem with regards to using a normal test project due to incompatible dll’s. I tried for almost a day before giving up to use a normal unit test project. One disadvantage of using the Silverlight Unit Test application is we don’t get code coverage result. I hope Microsoft will address this issue in their next release. Here is an output of the unit test related to view model run through Silverlight unit testing application.
The output isn’t very appealing. But still something is better than nothing. With these changes I have completed a major refactoring. This is how the application looks. There is not much change in the visual appearance but for sure it has become more maintainable
Refactoring 6 – Use NBuilder to generate test data
Every time we have a complex object graph, it comes difficult to build these objects while unit testing. One of the basic rule of unit testing is to arrange the inputs. Since each test is meant to test one single unit of execution, it might not require all the objects which are related to the parent object. Also if we are dealing with lists its cumbersome to generate lists by hand. Long time back I had blogged about using an open source tool called NBuilder to generate test data. I have used the same here for some of the tests and I intend to continue using it for the future tests.
Conclusion
Addition of service layer tests and separation of concerns for the UI layer were the main objectives of this refactoring. This gives a good safety net to experiment with the application. Integrating MVVM Light and Moq was much simpler than expected.
I did not find much difference between Moq and Rhino Mocks. The syntax looks cleaner in Moq and there is no concept of Record and Play as with Rhino Mocks. But recent versions of Rhino Mocks support AAA style of mocking which is almost similar to what Moq offers.
There were few bonuses as well that came along with MVVM Light in the form of Messenger and DispatcherHelper. In its current state the UI layer is dependent on ViewModel which interacts with the service layer. There can be few more improvements done down the line. I hope this has provided a good foundation for me to build upon the next set of features.
As always the complete working solution is available for download.
Until next time Happy Programming
No comments:
Post a Comment