Refactoring an existing Windows 8 Style App for Testability

Posted by: Raj Aththanayake , on 4/18/2013, in Category Windows Store Apps
Views: 37171
Abstract: Explore some key concepts that involves making a testable Windows 8 style app (XAML/C#)

Microsoft has heavily invested in this exciting new platform for developing Windows 8 style applications. If you are new to Windows 8 style application development, I suggest you refer to Develop great Metro style apps for Windows 8 article. Since Windows 8 style application development is becoming popular, there is also a strong emphasis on the testability of these types of applications. In this article, we will look at how to refactor an existing Windows 8 style application for better testability. We will also cover some of the key challenges we face, and how to overcome those challenges so we can make a testable Windows 8 style app. We will be using the new VS2012 for our demonstration purposes.

dncmag This article is published from the September Edition of the DNC Magazine – A Free High Quality Digital Magazine for .NET professionals published once every two months.

The September edition featured hot topics like Windows 8 Apps in all shape, form and size - WinRT+C#+XAML, WinRT+WinJS, WinRT+Testing and even a Tiled Web Framework! Apart from that we had WCF 4.5, Testing Benefits, Azure Caching and Hadoop On Azure too! Last but not least, our special guest in the Interview chair. The one and only Jon Skeet!.

Click Here to Download all the issues of this Free .NET Magazine

File Access Demo – Windows 8 style app

Shown here is a simple File Access application using WinRT, which allows us to select files from the user’s document folder and display the content of the file within the “Original File Content” text block. We assume the file contains only numbers (comma delimited) and we use the “Calculate Total” button to calculate the sum of the numbers.

DemoApp

(Note that my UI is not that fancy, I want to concentrate on the testing side of things)

Let’s look at a portion of the XAML mark-up that renders the above UI.


Text="Select File"
Foreground="Black"
FontFamily ="Verdana"
FontSize="20"
Margin="0,90,0,0"  />

SelectedItem="Name"                        
DisplayMemberPath="Name" 
Width="400"
HorizontalAlignment="Left"
Margin="0,10,0,0" 
SelectionChanged="cboSelectFile_SelectionChanged" />


Text="Original File Content"
Foreground="Black"
FontFamily ="Verdana"
FontSize="20"
Margin="450,-68,0,0"  />

Background="Azure"
Height="200"
Width="800"
HorizontalAlignment="Right" 
Margin="0, -32 630, 55" >
   x:Name="LblReadOriginalFile" 
TextWrapping="Wrap"
Style="{StaticResource H5Style}"
Width="2000"
Height="100"
HorizontalAlignment="Left" />


   x:Name="btnCalculateTotal"
Content="Calculate Total" 
Height="50" Width="170"
FontSize="20"
Margin="0,-290,0,0"
Click="btnCalculateTotal_Click"   />
               

Text="Total"
Foreground="Black"
FontFamily ="Verdana"
FontSize="20"
Margin="0,-50, 130, 55"  />

Background="Azure"
Height="200"
Width="300"
HorizontalAlignment="Left"
Margin="0,-40,130, 55"  >

     x:Name="LblReadFile"
TextWrapping="Wrap"
Style="{StaticResource H3Style}"
Width="991" Height="100"
HorizontalAlignment="Left" />

Here is the code behind file.

public sealed partial class MainPage : Page
{
private readonly StorageFolder storageFolder;

public MainPage()
{
    this.InitializeComponent();
    storageFolder = KnownFolders.DocumentsLibrary;
    AddFiles();
}

private async void AddFiles()
{
    var filesList = await storageFolder.GetFilesAsync();
    cboSelectFile.ItemsSource = filesList;
}


private async void
cboSelectFile_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
    var selectedItem = ((ComboBox)sender).SelectedItem;
    LblReadOriginalFile.Text = await FileIO.ReadTextAsync((IStorageFile)selectedItem);
}

private void btnCalculateTotal_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    var fileContent = LblReadOriginalFile.Text;

    if (!string.IsNullOrEmpty(fileContent))
    {
    var values = fileContent.Split(',');
    LblReadFile.Text = values.Sum(x => int.Parse(x)).ToString();
    }
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}

You might have already noticed that we have new Windows API references, which access the File System within the code behind. For example:

KnownFolders.DocumentsLibrary
storageFolder.GetFilesAsync();
FileIO.ReadTextAsync(..)

There is also some logic, for example splitting up the file content and summing up within the btnCalculateTotal_Click event.

Now let’s say, if we want to write a Unit Test to verify the logic where we calculate the total. We can spin-up a new instance of the “MainPage” object, and try to Unit Test the private btnCalculateTotal_Click event. Even before I go any further, you can understand that this is not going to be good Unit Testing experience. We feel like it is just wrong. And of course, we are not really writing a Unit Test, but we are testing the User Interface. Since there is no clear separation of concern between the UI and the business logic, it is almost impossible to test the total calculation logic.

Make File Access Demo App Testable

MVVM (Model-View-ViewModel) is a very well known pattern that has been around for a while. It has been widely used by WPF/Silverlight developers. This is the same for new Windows 8 style apps. In other words, you can apply the same MVVM concepts and patterns to Windows 8 style apps development.

Since there are plenty of resources on MVVM pattern, I will only briefly describes the usage of MVVM and put more emphasize on the testing of Windows 8 style apps.

First let’s briefly look at how to refactor this Windows 8 style app, so we can test the calculate logic in isolation.

The View Model

By introducing a ViewModel, we can provide a clean separation between the UI elements and the presentation logic. The ViewModel is an abstraction of the XAML View, and plays a key role in Data Binding. ViewModel implements the INotifyPropertyChanged interface (please see below). By implementing this special interface, we can take the advantage of the XAML data binding capabilities. It keeps a track of the state of the View such as, loading files, changing the drop down list, button click event etc. If you are a WPF/Silverlight Developer, there is absolutely nothing new here. Time to get excited, as you can apply your existing skills to develop a testable Windows 8 style app.

public class FileDataViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

private string fileContent;
public string FileContent
{
    get { return fileContent; }
    set
    {
    fileContent = value;
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs("FileContent"));
    }
    }
}

private ICommand calculateCommand;
public ICommand CalculateCommand
{
    get
    {
    if (calculateCommand == null)
    {
        calculateCommand = new CalculateCommand();
    }
    return calculateCommand;
    }
}

//more property bindings (see the complete code sample)..       
}

The above is a portion of the FIleViewModel class. There are other properties which I can bind to, but for the purpose of the demo, I will only consider the above 2 properties. In order to make the data binding work, the existing XAML elements require some changes as below.

Text="{Binding FileContent}" TextWrapping="Wrap" Style="{StaticResource H5Style}" Width="2000" Height="100" HorizontalAlignment="Left" />
Command="{Binding Path=CalculateCommand}" CommandParameter="{Binding}" Content="Calculate Total"  Height="50" Width="170" FontSize="20" Margin="0,-290,0,0" />

These declarative bindings will ensure that these properties can bind to the ViewModel properties when the data binding occurs. Also whenever a View Model’s property value has been changed, it raises property change event, which we have subscribed to. This event can be used to notify the new value to the XAML data binding system.

CalculateCommand

The above ViewModel’s, CalculateCommand returns an instance of the ICommand interface. This command also binds to the button. The CalculateCommand class implements behavior of the click event.

public class CalculateCommand : ICommand
{

public void Execute(object parameter)
{
    var model = (FileDataViewModel)parameter;

    if (!string.IsNullOrEmpty(model.FileContent))
    model.TotalValue = model.FileContent.Split(',').Sum(num => int.Parse(num)).ToString();
}

public bool CanExecute(object parameter)
{
    return true;
}

public event EventHandler CanExecuteChanged;
}

There are few ways of achieving the same result that I have implemented above - for example taking a service/model reference to the command class or passing as a delegate which contains the logic to execute etc. However to make the code simple, I have the logic implemented within the Execute method. What’s important to understand here is that we do not have calls to private methods, or UI elements, which we saw earlier with the UI code behind file. Therefore we can test this logic in isolation. Execute method accepts an object parameter which is the ViewModel.

Unit Testing the calculate logic

Now we can easily test the logic that requires summing up the file content as below.

[TestMethod]
public void CalculateCommand_WhenValidDataValueExists_ReturnsExpectedTotal()
{
    //Arrange           
    string validFileContent = "3, 5";
    string expectedTotal = "8";

    var model = new FileDataViewModel() { FileContent = fileContent };
    var sut = new CalculateCommand();

    //Act
    sut.Execute(model);

    //Assert            
    Assert.AreEqual(expectedTotal, model.TotalValue);
}

So far we saw pretty familiar stuff if you are a WPF/Silverlight Developer. But the key take away is you can utilize your existing MVVM skills with Windows 8 style apps. Let’s look at some of the metro specific testing.

Defining a file repository and testing Windows API

By introducing a repository class, we can abstract the direct access to file system via Windows API. This makes the testing of the domain model much easier, as we can stub out the repository during the Unit Testing.

If you take a look at the XAML code behind file, which I demonstrated earlier, there are some asynchronous calls that have been made to make the metro app responsive. The new Windows API, there are over 80% of WinRT calls, designed to call asynchronously. This also means that we can take the advantage of the new async, await keywords (.NET 4.5). New repository methods can also make async aware and the implementation can be simplified as shown below.

public interface IFileRepository
{
Task> GetFilesAsync();
Task GetFileContentAsync(FileItem storageFile);
}
public class FileRepository: IFileRepository
{              
private readonly StorageFolder storageFolder;

public FileService()
{
    this.storageFolder = KnownFolders.DocumentsLibrary;
}

public async Task> GetFilesAsync()
{                       
var files = await storageFolder.GetFilesAsync();
return files.Select(x => new FileItem(x.Name, x.DisplayName));
}

public async Task GetFileContentAsync(FileItem fileItem)
{
    var storageFile = await storageFolder.GetFileAsync(fileItem.Code);
    return await FileIO.ReadTextAsync(storageFile);
}
}

What I’m about to show you is an integration type test. This is because the test is going to touch the actual file system (via Windows API) and therefore it is not a Unit Test. Our intention of the test is to verify whether the GetFilesAsync method returns a list of file items/IEnumerable.

The first step is to create a Test project. However we cannot use the standard .NET MS Test project. Windows 8 style apps required a special type of Unit Test project. This is important to know because of a couple of reasons.

a. The Windows 8 style apps have limited access to the standard .NET API. It only uses a sub set of .NET API.

b. The Windows 8 style apps run in an AppContainer Sandbox. The app runs on its own container/app domain and does not have access to the outside world.

If you look at the new VS2012 project templates, there is a special test project template for Windows 8 style apps.

projecttemp

testproj

You notice that there is a .NET API for Windows 8 style apps, Testing components, and the Windows Runtime. There is also a new file Package.appxmanifest file. We will look at this file in more details bit later.

First I will write the test as below.

[TestMethod]
public void GetFilesAsync_WhenFilesExist_ReturnsListOfFiles()
{
    //Arrange
    var sut = new FileRepository();

    //Act
    var result = sut.GetFilesAsync();

    //Assert
    Assert.IsTrue(result.Any());
}

Obviously this code would not compile because GetFilesAsync returns an awaitable Task. Writing tests for asynchronous method has never been easy before. It makes the tests harder to read and maintain. It can also introduce bugs within the test because of the complexity. Fortunately the new MS Unit Testing framework and also xUnit.NET have simplified asynchronous testing a lot. The same keywords, async and await can be used within your test to test the asynchronous code. Please see below.

[TestMethod]
public async Task GetFilesAsync_WhenFilesExist_ReturnsListOfFiles()
{
    //Arrange
    var sut = new FileRepository();

    //Act
    var result = await sut.GetFilesAsync();

    //Assert
    Assert.IsTrue(result.Any());
}

Let’s run the above Test method assuming it returns a list of files from the file system. However as you can see below, we get a “UnAuthorizedAccessException”.

FileAccessError

The reason for this exception is that the test runs in its own test context. It does not have the visibility of the metro configuration that runs under the production code. This is why we need a special type of Unit Test project for metro apps so we can configure the Package.appxmanifest file and our test can access the File System. Let’s open the Package.appxmanifest file and configure the Capabilities settings as below.

capablities.

Now if we run the Unit Test the test passes as below.

testpassIntergration

Mocking/Stubbing in Windows 8 style apps

In the WinRT environment, isolating your dependencies is not the same as the legacy Desktop environment. The popular isolation/mock object frameworks (i.e. Moq, RhinoMock, NMock etc.) currently are not compatible with Windows 8 style apps testing. One of the main reasons for the incompatibility is that there is limited access to the .NET BCL. The methods that require generating dynamic proxy instances on the fly are not available in the WinRT environment.

For example if we attempt to add Moq to the Windows 8 style Unit Test project, we get the following error.

moq error

You might have already familiar with the new Visual Studio Fakes framework. This works greatl with non-metro apps. However currently Visual Studio Fakes does not have any support for Windows 8 style testing.

Below is a work around for your Windows 8 style applications using Moq and MoqRT

MoqRT

MoqRT is an open source library that was developed by Matthew Baxter. It is exactly the same as the Moq isolation framework, but it has been compiled against the WinRT library. The key difference is that MoqRT does not generate mock objects on the fly. It uses the .NET Remoting to listen to the new build within the test assembly and generates dynamic proxy assemblies (a process called baking). It is still fairly new (alpha release) so there may be some bugs you might come across.

I personally use this for Windows 8 style apps testing isolation needs and I find it quite useful. It is bit trickier to set it up, but once you have configured it, you can Moq APIs as you would normally do with the standard Moq. If you are interested setting up MoqRT, I encourage you to read the “Getting Started” section.

Baking Assembly and MoqRT

Now assume if the FileRepository requires an ILoggerService and we want to verify whether the Log method has been called only once.

The system under test

public FileRepository(ILoggerService loggerService)
{
    this.loggerService = loggerService;
}
public async Task> GetFilesAsync()
{
    loggerService.Log("retrieving files");

    var files = await storageFolder.GetFilesAsync();

    return files.Select(x => new FileItem(x.Name, x.DisplayName));
}
Test using MoqRT
[TestMethod]
public async Task GetFilesAsync_VerifyLogMethodIsCalled()
{
    //Arrange
    var loggerServiceMock = new Mock();
    var sut = new FileRepository(loggerServiceMock.Object);

    //Act
    var result = await sut.GetFilesAsync();

    //Assert
    loggerServiceMock.Verify(c => c.Log(It.IsAny()), Times.Once());
}

As you see, we can use the Moq API as we would normally use in non-metro testing.

Summary

We looked at some of the key concepts that involved making a testable Windows 8 style app. We briefly utilized the MVVM pattern and made the Windows 8 style app testable. We also looked at areas such as testing asynchronous API, and Windows 8 style integration testing. Finally we briefly looked at the options we have with stubbing and mocking with Windows 8 style app development.

Download the entire source code of this article over here (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Raj Aththanayake is a Microsoft ASP.NET Web Developer specializing in Agile Development practices such Test Driven Development (TDD) and Unit Testing. He is also passionate in technologies such as ASP.NET MVC. He regularly presents at community user groups and conferences. Raj also writes articles in his blog http://blog.rajsoftware.com. You can follow Raj on twitter @raj_kba


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!

Categories

JOIN OUR COMMUNITY

POPULAR ARTICLES

FREE .NET MAGAZINES

Free DNC .NET Magazine

Tags

JQUERY COOKBOOK

jQuery CookBook