Alessandro Del Sole's Blog

/* A programming space about Microsoft® .NET® */
posts - 142, comments - 0, trackbacks - 0

My Links


Your host

This is me! This space is about Microsoft® .NET® and Microsoft® Visual Basic development. Enjoy! :-)

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.

Microsoft MVP

My MVP Profile

I'm a VB!

Watch my interview in Seattle

My new book on VB 2012

Order my book about VB 2012 on Amazon My book "Visual Basic 2012 Unleashed" is available. Click the cover!

My new book on LightSwitch!

Visual Studio LightSwitch Unleashed My book "Visual Studio LightSwitch Unleashed" is available. Click the cover!

Your visits

campusMVP.NET - Tutored online training for Microsoft developers

Follow me on Twitter!

Messenger me!

CyberInstaller Beta Tester

Download CIS 2008!!

CodePlex download Download my open-source projects from CodePlex!

Search the blog

Article Categories


Post Categories

.NET Framework


Help Authoring

Microsoft & MSDN

Setup & Deployment

Visual Basic 2005/2008/2010

WPF: Introducing the Model-View-ViewModel pattern for Visual Basic 2010 developers - part 9

So far we wrote a lot of code, in this blog post series related to MVVM in WPF with Visual Basic 2010. Of course there are a lot of things to improve before heading to the last part. This will also allow us discovering a great benefit from using MVVM, which is writing unit tests against the ViewModel. Let's start by refactoring some code.

Refactoring time!

Refactoring the code is something that is mainly about both ViewModels. OrdersViewModel is the one requiring more attention. The first thing we want to improve is how the code uses the RelayCommand class: since we also wrote a generic implementation it's a good idea to use this one instead of the non generic class. With that said let's move to the OrdersViewModel class placing the cursor in the code region that exposes properties of type ICommand. As an example let's start by the first one, then you will have to repeat the same work against all the other properties. Let's re-write them using RelayCommand(Of Order):

    Public ReadOnly Property DeleteCommand() As ICommand
            If _cmdDeleteCommand Is Nothing Then
                _cmdDeleteCommand = New RelayCommand(Of Order)(AddressOf DeleteExecute, AddressOf CanDeleteExecute)
            End If
            Return _cmdDeleteCommand
        End Get
    End Property

Obviously we must also change the signature of methods pointed via AddressOf, so that they receive a typed parameter instead of an Object. Here they are:

    Private Function CanDeleteExecute(ByVal param As OrderAs Boolean
        If Me.CustomerOrdersView Is Nothing Then Return False
        Return Me.CustomerOrdersView.CurrentPosition > -1
    End Function
      Private Sub DeleteExecute(ByVal param As Order)
    End Sub

Of course you need to perform the same action for all other methods. Next, let's move to the _customersView_CurrentChanged event handler. Here we can extract a new private method from the event handler's body so that we can better organize the code and providing a useful approach for unit tests (if you like):

    Private Sub _customersView_CurrentChanged(ByVal sender As ObjectByVal e As System.EventArgsHandles _customersView.CurrentChanged
        Me.UpdateOrdersViewAfterCustomerChanged(CType(Me.CustomersView.CurrentItem, Customer))
    End Sub

    Private Function UpdateOrdersViewAfterCustomerChanged(ByVal currentCustomer As CustomerAs Boolean
            Me.Orders = orderAccess.GetAllOrders(currentCustomer.CustomerID)
            Me.CustomerOrdersViewSource.Source = Me.Orders
            Me.CustomerOrdersView = CType(Me.CustomerOrdersViewSource.View, ListCollectionView)
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function

Again with regard to unit test creation, instead of invoking the CustomerDataService.GetAllCustomers from within the constructor, such an invocation can be made from a new method inside the ViewModel that can be also tested and its result will be assigned to the appropriate object. Thus, first let's move at class level the following declaration:

Private dataAccess As ICustomerDataService

Once done this, let's write the following method which returns the result of the call:

    Private Function GetAllCustomers() As IQueryable(Of Customer)
       Return Me.dataAccess.GetAllCustomers
    End Function

Then inside the constructor we need to replace the following code:

        Dim dataAccess = GetService(Of ICustomerDataService)()
        Me.orderAccess = GetService(Of IOrderDataService)()
        For Each element In dataAccess.GetAllCustomers

with this one taking advantage of the new method:

        Me.dataAccess = GetService(Of ICustomerDataService)()
        Me.orderAccess = GetService(Of IOrderDataService)()
        For Each element In Me.GetAllCustomers

Don't be afraid if you are missing something, in next blog post you will be able to download the full source code. About the OrderDetailsViewModel, we need to apply the usage of RelayCommand(Of Order_Detail). I leave this to you as an exercise, since they are the same steps shown at the beginning of this paragraph. 

Writing and running unit tests

As you probably know, unit tests allow developers to test code blocks in a way that is abstracted from the application context. This means that a unit test checks if a code block works without running the application, in a fully separated context via specific tools from Visual Studio 2010. Writing unit tests is a best practice and the MVVM pattern provides the best support. In fact, how would you write unit tests against code that is associated to Views? Maybe you can do it, but this is in the UI context and is not correct. Now think of the  GetAllCustomers method that we implemented before. It resides in the ViewModel, so it is separated from the UI but it can access data. So it is possible to test if it works via a unit tests checking if it actually loads data, without interfering with the UI. Even if this sample is very simple, it will let you understand this benefit. With that said, go to the OrdersViewModel class and right click the GetAllCustomers method, then select Create Unit Test. Now you will get the dialog represented in the following figure, where you can select additional methods to test and specify a test project name:

At this point Visual Studio 2010 generates the project test, and the first thing to do is copying the App.config file from the primary project to the test project since it stores data connection information. Next, in the OrdersViewModelTest.vb code file let's search for the GetAllCustomersTest method. Our goal is testing if GetAllCustomers actually loads data from the database, thus returning a non-null value. Because of this the test method can be re-written as follows:

    <TestMethod(), _
     DeploymentItem("adsMVVM_EntityFramework_Complete.exe")> _
    Public Sub GetAllCustomersTest()
        Dim target As OrdersViewModel_Accessor = New OrdersViewModel_Accessor() ' TODO: Initialize to an appropriate value
        Dim actual As IQueryable(Of Customer)
        actual = target.GetAllCustomers
    End Sub

Now, using the appropriate Visual Studio tooling (or simply right-clicking inside the code and then Run Tests) let's run the test. As you can see, the unit test passes:

This is because the GetAllCustomers method actually loaded data and thus returns something not null, adhering to Assert requisites. Now you should understand why having a ViewModel is also useful: you can test code, even when you don't have a UI.

End of the story

In next post we will finally complete our work, preparing Views that is the UI, seeing also some messages exchange, a lot of data-binding and how View is not "no code" but it is "UI code".


Print | posted on giovedì 12 agosto 2010 17:46 | Filed Under [ Visual Studio 2010 Visual Basic Windows Presentation Foundation ]


No comments posted yet.

Post Comment

Please add 4 and 6 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET