Chapter 22. Unit Testing


In This Chapter

Creating unit tests with the Silverlight Unit Testing Framework for Windows Phone

Using the tag expressions editor

Test metadata and assertions

Windows Phone Test Driven Development

Code driven UI tests

The Microsoft Automation framework

Asynchronous tests

Using a custom IoC container

Testing of trial applications

Mocking launchers and choosers


Unit testing saves time and helps to find defects early in the development cycle. Unit tests become assets that provide assurance that further development or refactoring has not broken something. The more tests you create, the more confidence you have when adding features and fixing bugs. This is especially true if you are working with a team of developers.

Manual ad hoc testing becomes less effective as an app increases in size and complexity. Having a solid suite of unit tests can actually decrease the time it takes to get your app to the marketplace. This is because often an exorbitant amount of time is spent on ad hoc testing in the last stages of development before a release.

Another benefit of unit tests is that they can act as a tacit form of documentation. There is a distinct lack of code documentation in many software houses today; unfortunately it is all too common. Unit tests can assist a developer in understanding how an app works, and unit tests are less susceptible than traditional system documentation to implementation drift, where design changes and feature creep can see the documentation of an app become outdated. Unit tests tend to fare better because they are verifiable. Relying solely on unit tests to document a system is, however, not wise. Unit tests should be considered production code, requiring their own adequate documentation.

For all its benefits, unit testing does have a cost. It takes time and skill to write effective unit tests. In your career, you may have experienced the reluctance of management to support unit testing because it is invariably seen as time stolen from writing product code. It is that old story: Developers craft a product and then scramble in the last moments to iron out defects, producing fixes that frequently introduce new bugs. Unit testing can help to alleviate that last minute scramble.

Some may also see unit tests as a liability because when there are substantial changes to an app, unit tests have to be rewritten. This cost is, however, usually overstated and can sometimes reflect unsound development practices.

Testing and patterns of testing vary, and divided opinion on the topic has spurred many a heated debate. This chapter is not about affirming one approach over another. The techniques and tools presented in this chapter should, however, make a worthy addition to your development toolbox.

Throughout this book you have seen the use of Model-View-ViewModel (MVVM). MVVM comes into its own when combined with unit testing. In fact, it is one of the key motivations for using the pattern. MVVM allows you to separate UI technology specific code so that it can be tested without the user interface.

This chapter delves into the Silverlight UTF (Unit Testing Framework) for Windows Phone. The chapter begins with a walk-through of the creation of a unit test project from scratch. You see how to create test classes and test methods, and get to know the tag expressions editor, which provides a useful mechanism for selecting which tests are to be run.

The UTF is then explored more deeply by creating a simple chat client app, in which you see how to verify a viewmodel before creating its UI. Following the creation of the view, the test suite is extended to include code driven UI tests, where we manipulate the user interface, simulating button clicks and other user actions from a unit test. The chapter illustrates how to perform asynchronous testing and touches on the Microsoft Automation framework.

Some advanced topics such as Inversion of Control (IoC) and mocking are also discussed, and you learn how to use a custom IoC container.

Finally, we put it all together to see how to perform testing of trial applications and how to hide or reveal content based on a mock licensing service. You then look at a custom API for mocking launchers and choosers.

There is a lot to cover in this chapter, and the tools and techniques presented here can help spot unintended side effects, increase the robustness of code, assist in focusing the development effort, and perhaps save you some time.

Automated Testing

By finding and guarding against defects, an automated test can save you many times the cost of creating it over the lifetime of a project. This chapter looks at three kinds of automated testing: unit testing, integration testing, and coded UI testing.

Unit Testing

A unit test verifies that a single unit of work, usually a class method, works correctly. Unit tests should generally avoid accessing the file system, database, or network resources. Types requiring this kind of access ideally rely on an abstracted API and have the types used in production substituted with stubs or mock types during testing. Unit tests should execute rapidly and cause no side effects that affect other unit tests.

Integration Testing

Integration tests verify that multiple classes function correctly together. Integration occurs after unit testing. It involves testing groups of classes that have been unit tested.

Coded UI Testing

Coded UI tests simulate a user interacting with the application. These tests rely on an automation API to allow programmatic access to UI elements. For example, an automation object can be used to raise a button’s Click event.

There are other types of tests as well, including acceptance testing, performance testing, and stress testing. You often use different tools or extensions to your testing framework for these types of tests. Despite some tests not being formally unit tests, it’s fine to use the existing unit testing framework and tools to perform other kinds of testing. The unit testing tools presented in this chapter are a good starting point for many other types of tests.

It is important to partition your tests by test type. For example, unit tests should execute quickly. By grouping integration tests with unit tests, you risk slowing the execution of the group, which may make running the tests tedious, resulting in avoidance of unit testing altogether by you or another developer. Other test types, such as system tests, which test the entirety of your app, may rely on resources that are unavailable during a unit testing session. By including a system test with your unit tests, you may inadvertently prevent the test suite from passing.

Introduction to the Windows Phone Unit Test Framework

The API of the Silverlight UTF for Windows Phone is the same as the Silverlight UTF for the browser. In fact, it is almost a superset of the Microsoft desktop CLR UTF. This cross-screen compatibility means that you can run all your existing unit tests just as they are.

Unfortunately tooling support for the Silverlight UTF for Windows Phone is nonexistent. Unlike the Microsoft desktop UTF, there is no Visual Studio integration whatsoever. This means you cannot run individual unit tests from Visual Studio as you might with a desktop application. Windows Phone test projects are Silverlight for Windows Phone applications. Therefore, do not bother trying to create a new unit test from the Test menu in Visual Studio, as it is unsupported for Windows Phone.

While the tooling support is absent, all of the metadata and assertions, discussed later in this chapter, are identical for both the desktop CLR and Silverlight for Windows Phone, and creating a unit test project is easy, as you see in the following section.

Creating a Test Project

Download the Silverlight UTF for Windows Phone from Microsoft’s Jeff Wilcox’s blog at http://bit.ly/krk41Q. These assemblies are also present in the downloadable sample code, but to be sure you have the latest, download them from Jeff’s blog.

The download contains two assemblies: Microsoft.Silverlight.Testing.dll and Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight.dll.

As stated previously, a unit test project is a bona fide Windows Phone application. To create a test project, create a new Windows Phone Application project by using the Add New Project dialog (see Figure 22.1).

Image

Figure 22.1. Creating a new Windows Phone Application for testing

Once the test project has been created, add a reference to the two assemblies.


Tip

If you are working in a team and using a source control system, I recommend checking-in all non-FCL assemblies, regardless of whether an installer exists to place the assemblies in a known location. This allows you to propagate updated assemblies easily and helps prevent issues around incorrect installer versions.


Some changes need to be made in the new project’s MainPage.xaml.cs. The OnNavigatedTo method override should disable the system tray so that it does not intrude on the UTF test harness. A test page can then be created using the Microsoft.Silverlight.Testing.UnitTestSystem.CreateTestPage method (see Listing 22.1).

The app’s root visual is assigned to the test page instance. Windows Phone does not allow the assignment of the RootVisual in App.xaml.cs. This is why the code to create the test page is placed in the MainPage class and not in the App class.

The BackKeyPress method is overridden so that pressing the hardware button causes the IMobileTestPage object’s NavigateBack method to be called. If the NavigateBack method returns false, such as when the first test page is displayed, then the navigation is cancelled, preventing navigation.

Listing 22.1. Test Project MainPage.xaml.cs


public partial class MainPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    IMobileTestPage mobileTestPage;

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        SystemTray.IsVisible = false;

        UnitTestSettings settings = UnitTestSystem.CreateDefaultSettings();
        /* To set the TagExpression use the following: */
        // UnitTestSettings settings = UnitTestSystem.CreateDefaultSettings();
        // settings.TagExpression = "UnitTest";

        /* Add test assemblies as shown: */
        // settings.TestAssemblies.Add(typeof(Class1).Assembly);
        // settings.TestAssemblies.Add(typeof(TestClass2).Assembly);

        UIElement testPage = UnitTestSystem.CreateTestPage();

        Application.Current.RootVisual = testPage;
        Application.Current.Host.Settings.EnableFrameRateCounter = false;
        mobileTestPage = testPage as IMobileTestPage;
    }

    protected override void OnBackKeyPress(CancelEventArgs e)
    {
        if (mobileTestPage != null)
        {
            e.Cancel = mobileTestPage.NavigateBack();
        }

        base.OnBackKeyPress(e);
    }
}


Creating a Test Class

To create a test class, add a new class to your test project. Then add the following two using statements to the top of the class:

using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Test related attributes and assertion types can be found within the namespace Microsoft.VisualStudio.TestTools.UnitTesting. Even if you have not used the Visual Studio UTF before, it is easy to pick up since the attributes are self-descriptive.

Tests consist of test classes and test methods. The unit test framework relies on metadata in the form of attributes for identifying unit test classes and methods at runtime. To indicate that a class is a unit test class, decorate it with the TestClassAttribute, as shown in the following example:

[TestClass]
public class FirstTest : SilverlightTest
{
    [TestMethod]
    [Description("A first unit test.")]
    public void ShouldAlwaysPass()
    {
        Assert.IsTrue(true);
    }
}

To run the test, set the test project as the startup project by right-clicking on the project node in the Visual Studio Solution Explorer and selecting Set as Startup Project. Then debug the solution by selecting Start Debugging from the Debug menu or by pressing F5. When the app starts, the tag expressions editor is presented (see Figure 22.2).

Image

Figure 22.2. The tag expressions editor

The tag expressions editor allows you to run all or a subset of the tests. It has a countdown timer that automatically commences testing after 5 seconds.

When the test completes, a review page is presented in the test harness (see Figure 22.3).

Image

Figure 22.3. The test harness displays that the unit test passed.

The test harness allows you to drill down to see the details of each test method (see Figure 22.4). The content of the test method’s Description attribute is presented beneath the test method’s name.

Image

Figure 22.4. Viewing a detailed test result

Tests run on a single thread and are executed within the Silverlight sandbox.

Tag Expressions

When you have a large number of tests, you need some way to execute just one, or a subset, of the tests. The UTF includes a tagging language, which allows you to create tag expressions that specify which tests should be run during a unit testing session.

Every test class and test method has an implicit set of tags, shown in the following list:

• Type or method name, for example, TestClass1, TestMethod1.

• Full type or method name, for example, TestClass1.TestMethod1.

• The priority specified by a Priority attribute if present. For further information on tag expressions and the Priority attribute, see the section “Priority Attribute” later in the chapter.

You can also explicitly assign a test class or test method with a tag using the Tag attribute. The Tag attribute accepts a single string parameter, which is used to associate a test with a group of tests.

The following is an example of a test method decorated with a Tag attribute:

[TestMethod]
[Tag("UITest")]
public void AlwaysPass()
{
    Assert.IsTrue(true, "Test method intended to always pass.");
}

Multiple Tag attributes can be applied to any test class or method in your test suite. Tags can also be useful in selecting different kinds of tests to run.

When the testing session begins, you have the opportunity to enter a tag expression. To run the test method from the previous excerpt, or any other test methods with a matching attribute, you could enter UITest as the tag expression.

The tag expression syntax provides a set of operators to control test selection. The ! (not) operator, for example, allows you to prevent those tests with a particular tag from being executed. A tag expression of !UITest causes all tests that do not have the tag UITest to be executed. A tag of All-(UITest+IntegrationTest) causes all tests that do not have the UITest or IntegrationTest tag to execute.

The tag expression syntax uses Extended Backus-Naur Form (EBNF). You can use the symbols presented in Table 22.1 in tag expressions.

Table 22.1. Tag Expression Symbols

Image

Setting the Tag Expression Programmatically

To set the tag expression without using the UTF tag expressions editor, use the TagExpression property of the UnitTestSettings class, as shown in the following example:

void OnLoaded(object sender, RoutedEventArgs e)
{
    SystemTray.IsVisible = false;

    UnitTestSettings settings = UnitTestSystem.CreateDefaultSettings();

    /* To set the TagExpression use the following: */
    UnitTestSettings settings = UnitTestSystem.CreateDefaultSettings();
    settings.TagExpression = "UnitTest";
    UIElement testPage = UnitTestSystem.CreateTestPage(settings);

    Application.Current.RootVisual = testPage;
    Application.Current.Host.Settings.EnableFrameRateCounter = false;

    mobileTestPage = testPage as IMobileTestPage;
}

Metadata and Assertions

As you have seen, attributes are used to identify test classes and methods. Attributes can also be used to provide other metadata information to the UTF. This section examines each of the UTF attributes in greater detail and provides descriptions of their usage, purpose, and effect on test execution.

TestClass Attribute

The TestClass attribute indicates that a class contains test methods.

TestMethod Attribute

The TestMethod attribute indicates that a method should be included in the suite of tests. The Ignore attribute can be used to temporarily exclude a test method from the suite of tests. Methods decorated with the TestMethod attribute should be public and have a void return type.

Metadata for Test Initialization and Cleanup

The UTF provides attributes that allow you to specify methods that should be run before and after test execution. You can provide methods that are executed once for each test assembly, once for each test class, and once for each test method. The order in which methods decorated with each particular attribute are run is presented in Figure 22.5.

Image

Figure 22.5. Test execution order is affected by initialization and cleanup metadata.

AssemblyInitialize Attribute

The AssemblyInitialize attribute identifies a method that contains code to be used before all tests in an assembly are run and to allocate resources obtained by the assembly. A method decorated with the AssemblyInitialize attribute must be public and static, and have a void return type. The following demonstrates the use of the AssemblyInitialize attribute:

[AssemblyInitialize]
public static void AssemblyInitialize()
{
    /* Assembly initialization logic goes here. */
}


Note

The test framework runs a method that is marked with the AssemblyInitialize attribute only if that method is a member of a class that is marked with the TestClass attribute.


AssemblyCleanup Attribute

The AssemblyCleanup attribute is analogous to the AssemblyInitialize attribute but occurs at the end of the test run. As with the AssemblyInitialize attribute, a method decorated with this attribute should be located in a test class. The following shows an example of a method decorated with the AssemblyCleanup attribute:

[AssemblyCleanup]
public static void AssemblyCleanup()
{
    /* Assembly cleanup logic goes here. */
}

ClassInitialize Attribute

The ClassInitialize attribute provides the opportunity to run code before any of the tests in the test class have run and to allocate resources to be used by the test class.

A method decorated with the ClassInitialize attribute must be public and static with a void return type. Only one method in a class may be decorated with this attribute.

The following shows an example of a method decorated with the ClassInitialize attribute:

[ClassInitialize]
public static void ClassInitialize()
{
    /* Class initialization logic goes here. */
}

ClassCleanup Attribute

The ClassCleanup attribute is analogous to the ClassInitialize attribute but occurs after all test methods have completed within the test class. The following shows an example of a method decorated with the ClassCleanup attribute:

[ClassCleanup]
public static void ClassCleanup()
{
    /* Class cleanup logic goes here. */
}

TestInitialize Attribute

The TestInitialize attribute is used to indicate that a method decorated with this attribute should be called before every test method within a test class. A method decorated with the TestInitialize attribute must be public and have a void return type. The following shows an example of a method decorated with the TestInitialize attribute:

[TestInitialize]
public void TestInitialize()
{
    /* Test initialization logic goes here. */
}


Tip

If more than one method is decorated with the TestInitialize attribute in a test class, it prevents the execution of all test methods. Furthermore, it does so silently. If you find that the debugger is failing to hit a break point in a test method, look for a duplication of the TestInitialize attribute.


TestCleanup Attribute

The TestCleanup attribute is useful for resetting the state of shared resources in between test methods, such as an object that is used by all tests within a test class.

[TestCleanup]
public void TestCleanup()
{
    /* Test cleanup logic goes here. */
}

Miscellaneous Metadata

The following attributes allow you to control various other aspects of test execution.

TestProperty Attribute

The TestProperty attribute allows arbitrary metadata to be associated with a test method. For example, you could use it to store the name of a test pass that this test covers by decorating the test method with [TestProperty("TestPass", "Accessibility")]. Unlike the Silverlight UTF for the browser and the Visual Studio desktop CLR unit testing tools, the Windows Phone UTF does not display TestProperty information within the test harness.

Ignore Attribute

The Ignore attribute can be used to temporarily exclude a specific test from execution. This can be useful for excluding a test that is blocking other tests from running. It allows you to retain compilation of the test, rather than merely commenting out the code.


Note

The number of tests listed in the unit test harness is unaffected by the Ignore attribute.


Description Attribute

The Description attribute is used on test methods and allows you to provide a string describing the purpose and/or behavior of the test method. The description is then presented beneath the title of the test result details screen on the phone (refer to Figure 22.4). The following example demonstrates the use of the Description attribute:

[TestMethod]
[Description("An example test demonstrating the Description attribute.")]
public void ShouldAlwaysPass()
{
    Assert.IsTrue(true);
}

Timeout Attribute

The Timeout attribute allows you to specify an amount in milliseconds in which a test method must complete or the test method fails.

The following is an example of using the Timeout attribute to prevent an asynchronous test method from taking longer than 100 milliseconds to execute:

[TestMethod]
[Asynchronous]
[Timeout(100)]
public void ShouldFailDueToAsyncTestTimeout()
{
    EnqueueDelay(1000);
    EnqueueTestComplete();
}

This test method fails because the call to EnqueueDelay delays the completion of the test by 1000 ms (1 second), and the Timeout attribute specifies that the test should take no longer than 100 ms. The EnqueueDelay attribute and the other asynchronous related attributes are discussed later in this chapter.

Owner Attribute

The Owner attribute is used to specify the person responsible for maintaining, running, and/or debugging the test. This attribute accepts a single string parameter, indicating the name of the owner, as shown in the following example:

[TestMethod]
[Owner("Daniel Vaughan")]
public void ShouldAlwaysPass()
{
    Assert.IsTrue(true);
}

The value of the attribute is not displayed within the test harness of the current Windows Phone UTF.

ExpectedException Attribute

Verifying that your code produces a correct response to a known set of values is one thing; verifying that it responds appropriately when given bad input is another. This is called negative testing, and it allows you to verify that code behaves correctly even when exceptional conditions arise.

Ordinarily, if a test method raises an exception, the exception causes that test to fail. In a negative test, however, raising an exception may be the expected behavior. In that case, the ExpectedException attribute can be used to indicate that if an exception of a particular type, with a particular message, is not thrown, then the test should fail. The following example demonstrates the use of the ExpectedException attribute:

[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldFailDueToArgumentException()
{
    throw new ArgumentException("Invalid argument supplied.");
}


Note

The TagAttribute, AsynchronousAttribute, and BugAttribute are not available in the Microsoft desktop CLR test framework. Using any of them, therefore, means that your unit tests will not be compatible with the desktop CLR.


Asynchronous Attribute

The Asynchronous attribute informs the UTF that a test method should be considered to be in a running state until the SilverlightTest.EnqueueTestComplete method is called.


Tip

Use the Asynchronous attribute in combination with the Timeout attribute to prevent the test method from taking forever if it fails to call EnqueueTestComplete.


For more information on the Asynchronous attribute, see the section “Asynchronous Testing.”

Bug Attribute

The Bug attribute allows you to associate a known bug with a unit test. Its key characteristic is that it reverses the result of a test, so you can first use it to demonstrate the existence of a bug, and once the bug is resolved, the Fixed property indicates that the test should pass. For example, if a method is able to reproduce a bug in the software, it may first look like this:

[Bug("TFS 123")]
[TestMethod]
public void ExerciseBuggyCode()
{
    Assert.IsTrue(false); /* Simulates some broken code. */
}

Consider the preceding test method. It passes because the Bug attribute’s Fixed property is false by default.

When the issue is resolved, you can add the Fixed property to the Bug attribute, which removes the inversion behavior of the Bug attribute.

[Bug("TFS 123", Fixed = true)]
[TestMethod]
public void ExerciseBuggyCode()
{
    Assert.IsTrue(true); /* Simulates issue resolved. */
}

Priority Attribute

Contrary to first assumptions, the Priority attribute is not used by the test system. Its purpose can be defined by you. It is, however, added to the set of implicit expression tags. The attribute requires an integer constructor argument, which specifies the priority value. For example:

[TestMethod]
[Priority(1)]
public void AlwaysPass()
{
    Assert.IsTrue(true, "Test method intended to always pass.");
}

We can then use the tag expression Priority1 to execute this test method.

SilverlightTest: The Base TestClass Type

UTF test classes normally inherit from the SilverlightTest class. While inheriting from this class is not strictly required, it does provide for some advanced features such as asynchronous testing.

Asynchronous unit test methods are a key feature of the UTF for Silverlight, and one that is notably absent from the Microsoft UTF for the desktop CLR.


Note

By inheriting from SilverlightTest, you lose compatibility with the Desktop CLR Visual Studio unit test framework. Therefore, do not inherit from this class if you want to retain cross-screen compatibility and do not require advanced UI testing facilities, such as asynchronous tests.


Verifying Conditions with Assertions

Assertions are the cornerstone of UTF. The Assert class has a multitude of test related method overloads that allow you to ensure the validity of your app’s state and behavior. The following is the core set of assertions used by most test classes:

AreEqual and AreNotEqual—These methods rely on the Object.Equals method to determine object equality. There are various overloads for primitives, as well as reference types.

AreSame and AreNotSame—Tests for whether two variables refer to the same object instance. These methods rely on the Object.ReferenceEquals method. There are various overloads for primitives, as well as reference types.

Fail—Allows you to explicitly fail a test based on logic within the test method.

Inconclusive—Allows you to explicitly set the outcome of a test to inconclusive.

IsTrue and IsFalse—Verifies that a Boolean value is either true or false.

IsInstanceOfType and IsNotInstanceOfType—Verifies that an object instance does or does not inherit from a specified type.

IsNull and IsNotNull—Verifies that an object is, or is not, null.

If an Assert method fails, it raises a UnitTestAssertException, which is handled by the UTF infrastructure and reported back to you as a test failure.

To complement the set methods provided by the Assert classes, there exists a CollectionAssert class with methods particular to collections, and a StringAssert class, which provides assertions based on regular expressions.

Verifying Collection Conditions with CollectionAssert

The following is the list of collection assertions that enable you to verify the contents of collections:

AllItemsAreInstancesOfType—Verifies that all items in a collection are, or inherit from, a specified type.

AllItemsAreNotNull—Verifies that no item in the collection is null.

AllItemsAreUnique—Verifies that a collection is a set; each item occurs only once.

AreEqual and AreNotEqual—Verifies that two collections have the same number of items, and that each item in the first collection is equal to the item at the same index in the second collection.

AreEquivalent and AreNotEquivalent—Verifies that two collections have the same number of items and that each item in the first collection has an item that is equal to it in the second collection. This differs from AreEqual and AreNotEqual in that order does not matter.

Contains and DoesNotContain—Verifies that a collection contains a specified item.

IsSubsetOf and IsNotSubsetOf—Verifies that all items in one collection exist in another specified collection.

Verifying String Conditions with StringAssert

The StringAssert class provides various methods for verifying the contents of strings:

Contains—Verifies that a string contains a specified substring.

Matches and DoesNotMatch—Uses a regular expression to verify that the specified string matches, or does not match, a specified pattern.

StartsWith and EndsWith—Verifies that a string starts or ends with a specified string.

Hiding the Expressions Editor

Sometimes you may like to execute your tests without having to interact with the UTF interface. It can be annoying to have to tap the Use Tag button to kick of the unit tests before the countdown timer reaches zero.


Note

Unfortunately, the test harness does not allow you to hide the tag expressions editor in the current version of the Windows Phone UTF. Nonetheless, it is shown here in anticipation of a future release of the UTF.


To hide the tag expressions editor (in a future release) and to run the tests as soon as the test harness launches, you need to modify two properties in the UnitTestSettings class; set the StartRunImmediately property to true, and set the ShowTagExpressionEditor to false. This is demonstrated in this excerpt from the MainPage.xaml.cs file in the downloadable sample code:

void OnLoaded(object sender, RoutedEventArgs e)
{
    SystemTray.IsVisible = false;
    UnitTestSettings settings = UnitTestSystem.CreateDefaultSettings();
    settings.StartRunImmediately = true;
    settings.ShowTagExpressionEditor = false;

    UIElement testPage = UnitTestSystem.CreateTestPage(settings);
    Application.Current.RootVisual = testPage;
    Application.Current.Host.Settings.EnableFrameRateCounter = false;

    mobileTestPage = testPage as IMobileTestPage;
}

Testing Multiple Assemblies

The current implementation of the UTF for Windows Phone does not support inclusion of tests from assemblies outside the test project; all tests must reside in the Windows Phone test project.

When this shortcoming is rectified, if you want to have a single project for testing and include other test assemblies, add assemblies to the TestAssemblies collection of the UnitTestSettings class, as shown:

UnitTestSettings settings = UnitTestSystem.CreateDefaultSettings();
settings.TestAssemblies.Add(typeof(TestClass1).Assembly);
settings.TestAssemblies.Add(typeof(TestClass2).Assembly);
UIElement testPage = UnitTestSystem.CreateTestPage(settings);

Testing Non-Public Members

Sometimes, you need to test classes and members that are not public but are internal to a project, such as during system testing, where you need to interact with UI elements directly. To have access to internal members, you must allow the test project access to the internal members of the main project. You do this by placing an InternalsVisibleTo attribute into the AssemblyInfo class of the main project, as shown:

[assembly: InternalsVisibleTo("WindowsPhone7Unleashed.Tests.Silverlight")]

The InternalsVisibleTo attribute accepts a single parameter, which is the name of the assembly. If the test project has a strong name, then the full name of the assembly must be used, including its public key token.

Examples of the InternalsVisibleTo attribute are in the downloadable sample code.

A Testable Chat Client

This section explores a simple chat client app. First, a custom chat service that is used to send and receive messages is discussed. Next, you examine how to substitute types using mocking and look at mocking the chat service. You see how to test the functionality of a viewmodel without needing to create a view for it. Finally, you learn how to create coded UI tests using the UTF TestPanel in combination with the Microsoft Automation framework.

The code for this section is located in the ChatClientView and ChatClientViewModel classes in the downloadable sample code.

The chat client app relies on a chat service, represented by an IChatService interface. The IChatService specifies a method to send a message, presumably to a cloud service, and an event to indicate when the service receives a message. The interface is shown in the following excerpt:

public interface IChatService
{
    void SendMessage(string message);
    event EventHandler<ChatMessageEventArgs> MessageReceived;
}

A mock chat service is used during unit testing. Mock objects mimic the behavior of real objects in controlled ways. In this case, MockChatService acts as loopback; when the SendMessage method is called, the MessageReceived event is raised, as shown:

public class MockChatService : IChatService
{
    public bool MessageSent { get; private set; }
    public string LastMessage { get; private set; }

    public void SendMessage(string message)
    {
        MessageSent = true;
        LastMessage = message;
        OnMessageReceived(new ChatMessageEventArgs(message));
    }

    public event EventHandler<ChatMessageEventArgs> MessageReceived;

    public void OnMessageReceived(ChatMessageEventArgs e)
    {
        MessageReceived.Raise(this, e);
    }
}

The viewmodel for the chat client uses the chat service to send messages using a SendCommand (see Listing 22.2). When the SendCommand executes, the IChatService.SendMessage method receives the message.

The viewmodel subscribes to the IChatService.MessageReceived event. When a message is received, it is placed into an ObservableCollection of messages, which are then presented in the view.

Listing 22.2. ChatClientViewModel Class (excerpt)


public class ChatClientViewModel : ViewModelBase
{
    readonly IChatService chatService;

    public ChatClientViewModel(IChatService chatService)
    {
        this.chatService = ArgumentValidator.AssertNotNull(
                                        chatService, "chatService");

        sendCommand = new DelegateCommand(
            delegate
            {
                if (string.IsNullOrEmpty(message))
                {
                    return;
                }
                chatService.SendMessage(message);
                Message = string.Empty;
            },
            delegate { return !string.IsNullOrEmpty(message); });

        chatService.MessageReceived
            += (sender, args) => messages.Add(args.Message);

        PropertyChanged += delegate { sendCommand.RaiseCanExecuteChanged(); };

        /* The rest of the constructor is shown later in the chapter. */
    }

    readonly ObservableCollection<string> messages
                    = new ObservableCollection<string>();

    public ObservableCollection<string> Messages
    {
        get
        {
            return messages;
        }
    }
    string message;

    public string Message
    {
        get
        {
            return message;
        }
        set
        {
            Assign(() => Message, ref message, value);
        }
    }

    readonly DelegateCommand sendCommand;

    public ICommand SendCommand
    {
        get
        {
            return sendCommand;
        }
    }

    /* The rest of the class is shown later in the chapter. */
}


With the infrastructure in place, you can create some unit tests for the viewmodel.


Note

An alternative approach, known as Test Driven Development (TDD), sees the creation of the unit tests first. If you are willing to take an interface first approach or have a tool such as Resharper that allows you to quickly generate class members, you may choose to create the unit tests before you implement the class that is the subject of the unit tests.


Listing 22.3 shows various test methods for verifying that the viewmodel is able to send a message correctly and that it is able to respond correctly when it receives a message from the chat service.

Listing 22.3. Chat Client ViewModel Tests


[TestClass]
public class ChatClientTests : SilverlightTest
{
    [TestMethod]
    public void ShouldSendMessage()
    {
        string testMessage = "Hello from unit test.";
        MockChatService chatService = new MockChatService();
        ChatClientViewModel viewModel = new ChatClientViewModel(chatService);
        viewModel.Message = testMessage;
        viewModel.SendCommand.Execute(null);
        Assert.AreEqual(chatService.LastMessage, testMessage);
    }

    [TestMethod]
    public void ShouldNotSendMessageIfEmpty()
    {
        MockChatService chatService = new MockChatService();
        ChatClientViewModel viewModel = new ChatClientViewModel(chatService);
        viewModel.Message = string.Empty;
        viewModel.SendCommand.Execute(null);
        Assert.IsFalse(chatService.MessageSent);
    }

    [TestMethod]
    public void CommandShouldBeDisabledIfMessageIfEmpty()
    {
        MockChatService chatService = new MockChatService();
        ChatClientViewModel viewModel = new ChatClientViewModel(chatService);
        viewModel.Message = string.Empty;
        Assert.IsFalse(viewModel.SendCommand.CanExecute(null));
    }

    [TestMethod]
    [Description(@"When the chat service receives a message,
        the client displays it.")]
    public void CommandShouldBeEnabledIfMessageNotEmpty()
    {
        MockChatService chatService = new MockChatService();
        ChatClientViewModel viewModel = new ChatClientViewModel(chatService);
        viewModel.Message = "Test";
        Assert.IsTrue(viewModel.SendCommand.CanExecute(null));
    }

    [TestMethod]
    public void ShouldReceiveMessage()
    {
        string testMessage = "Hello from unit test.";
        MockChatService chatService = new MockChatService();
        ChatClientViewModel viewModel = new ChatClientViewModel(chatService);
        chatService.OnMessageReceived(new ChatMessageEventArgs(testMessage));
        CollectionAssert.Contains(viewModel.Messages, testMessage);
    }
}


The result of running the unit tests is shown in Figure 22.6.

Image

Figure 22.6. Chat client test results

Building the View

The view allows the user to send and receive messages via the viewmodel. The view consists of the following elements:

• A TextBox to allow the user to enter a message to send.

• An AppBar that includes an AppBarIconButton to execute the viewmodel’s SendCommand. For more information on the AppBar, see Chapter 8, “Taming the Application Bar.”

• A ListBox to display all incoming messages.

The following excerpt shows the main content from the ChatClientView.xaml page:

<StackPanel Grid.Row="1"
        Style="{StaticResource PageContentPanelStyle}">
    <u:AppBar>
        <u:AppBarIconButton
        Command="{Binding SendCommand}"
        Text="Send"
        IconUri="/ChatClient/Images/AppBarMessageSend.png"
        x:Name="button_Send"
        AutomationProperties.AutomationId="button_Send" />
    </u:AppBar>

    <TextBox x:Name="textBlock_Message"
        Text="{Binding Message, Mode=TwoWay,
        UpdateSourceTrigger=Explicit}"
        TextWrapping="Wrap" AcceptsReturn="True"
        u2:UpdateSourceTriggerExtender.UpdateSourceOnTextChanged="True"/>
    <TextBlock Text="Messages" Style="{StaticResource LabelTextStyle}"/>
    <ListBox x:Name="listBox" ItemsSource="{Binding Messages}" Height="400">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}"
                    Style="{StaticResource NormalTextStyle}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</StackPanel>

The various input controls have been named. You see later in this chapter how the naming of elements allows you to directly manipulate them from a coded UI unit test.


Tip

If you are not creating coded UI tests, it is better to refrain from naming elements unless you need to refer to them in the code-beside. This helps identify those elements that are referred to in the code-beside, and decreases the verbosity of the XAML.


The view’s code-beside (ChatClientView.xaml.cs) instantiates the viewmodel and supplies the viewmodel with an instance of the MockChatService, as shown in the following excerpt:

public ChatClientView()
{
    InitializeComponent();
    DataContext = new ChatClientViewModel(new MockChatService());
}

Later in the chapter you see how to replace the MockChatService for a real chat service implementation using Inversion of Control (IoC).

The final result of the chat page is shown in Figure 22.7.

Image

Figure 22.7. ChatClientView page

Code Driven UI Testing

A key benefit of the MVVM pattern is that it allows you to unit test an app without needing to display its UI. Sometimes, however, you may want to verify the behavior of the UI by emulating user interaction. This can be achieved using coded UI tests.

The advantage of coded UI tests is that they provide your app with a test environment that is closer to the real world; they allow you to simulate the tapping of buttons or entering of text while the app is running.

The downside of coded UI tests is that they are tightly coupled to the user interface, which is often the most likely thing to change over time. If the UI is modified, it can mean having to rewrite your coded UI tests.

Coded UI tests can be performed with the UTF by populating the SilverlightTest object’s TestPanel with the page or control that you want to test, as demonstrated in the following excerpt:

[TestClass]
public class ChatClientUITests : SilverlightTest
{
    ChatClientView view;

    [TestInitialize]
    public void PreparePage()
    {
        view = new ChatClientView();
        TestPanel.Children.Add(view);
    }
...
}

Recall that the TestInitialize attribute causes the method to be called before each test method in the parent class. You see that when the PreparePage method is called, it creates a new instance of the ChatClientView and places it into a container control within the test harness.

This section looks at the three test methods in the ChatClientUITests class.

The first test method simply verifies that the PhoneApplicationPage is presented in the TestPanel, as shown:

[TestMethod]
public void ShouldDisplayDefaultSize()
{
    Assert.IsTrue(view.ActualWidth > 0);
}

The ShouldDisplayDefaultSize method demonstrates that you are able to interact with the UI elements from code. In fact, while the tests are running their actions are viewable within the test harness (see Figure 22.8).

Image

Figure 22.8. A UI test in execution

To interact with controls on the page, give the test project access to the internal members of the main project. This is done by placing an InternalsVisibleTo attribute in the AssemblyInfo class of the main project, shown in the previous section “Testing Non-Public Members.”

When the InternalsVisibleTo is placed in the target project, IntelliSense is enabled for controls on the page.

To test that the Send button is enabled when the message TextBox is populated, set the text and then perform an assert using the button, as shown in the following excerpt:

[TestMethod]
public void ButtonShouldBeEnabled()
{
    view.textBlock_Message.Text = "Test";
    Assert.IsTrue(view.button_Send.IsEnabled);
}

Asynchronous Testing

The UTF is designed to run tests on a single thread. The Silverlight UI thread uses a message pump and often calls to update properties or reactions to property changed events are driven forward using Dispatcher calls. The result is that a UIElement may not be in the state that you think it should be when you are performing an assertion. Fortunately, the Silverlight UTF compensates with some built-in asynchronous features.

The Asynchronous attribute tells the UTF to keep running the test until the test class’s base method, SilverlightTest.EnqueueTestComplete is called or until an unhandled exception occurs. This allows you to spin off work to other threads or queue work to happen on the UI thread, while keeping the test alive.

The SilverlightTest.EnqueueCallback method allows actions to be queued on the UI thread. This may include actions such as verifying the value of a UIElement’s property after a PropertyChanged event has been given the opportunity to propagate.

The following excerpt demonstrates how to use the Asynchronous attribute in combination with the EnqueueCallback method to set the text of a sample view’s message box. It simulates a tap to the Send Message button and then verifies that the ListBox has been correctly populated:

[TestMethod]
[Asynchronous]
public void ButtonShouldSendMessage()
{
    view.textBlock_Message.Text = "Test";
    EnqueueCallback(() => ((IAppBarItem)view.button_Send).PerformClick());
    EnqueueCallback(() => Assert.IsTrue(view.listBox.Items.Count > 0));
    EnqueueTestComplete();
}

When the textBlock_Message.Text property is set, the Send button is not enabled until the viewmodel’s SendCommand responds to the Message property’s PropertyChanged event. Once it has had a chance to respond, the command enables itself.

The EnqueueCallback method queues the PerformClick method, so that by the time it is called, the button is enabled. Similarly, verification that the ListBox has been populated must occur after the UI has responded to the new item in the Messages collection in the viewmodel.

The UTF is notified that all nonblocking activity has been completed by calling the base class’s SilverlightTest.EnqueueTestComplete method.

Using Automation Peers to Manipulate UI Elements at Runtime

When performing code driven UI testing, often you need to manipulate elements in ways that are not possible using the API of the elements themselves. The Button class, for example, does not have a PerformTap method to raise the Tap event. The built-in Silverlight controls are generally well encapsulated and are not designed to simulate interaction via code. For this, we turn to the Microsoft UI Automation framework, which consists of a secondary API for manipulating UI elements from code. The Automation framework is designed for accessibility software, allowing third-party software to manipulate the UI on behalf of a user with a disability.

The Automation API is able to manipulate elements, such as a Button, using internal methods of the various built-in FrameworkElement types, which are ordinarily off-limits.

In this example, a button is placed on the ChatClientView page. When tapped, it sets the view’s custom ButtonClicked property to true. The XAML for the button is as follows:

<Button x:Name="button_AutomationTest"
        Content="AutomationTestButton"
        Click="button_AutomationTest_Click" />

The code-beside for the view contains the event handler for the Click event of the button:

public bool ButtonClicked { get; private set; }

void button_AutomationTest_Click(object sender, RoutedEventArgs e)
{
    ButtonClicked = true;
}

An AutomationPeer, located in the System.Windows.Automation.Peers namespace, is used to verify that the button was indeed clicked. The AutomationPeer object is then used to retrieve an IInvokeProvider specific to the Button class, which allows you to invoke the button’s Click event, shown in the following excerpt:

[TestMethod]
[Asynchronous]
public void DemonstrateButtonClick()
{
    AutomationPeer peer
              = FrameworkElementAutomationPeer.CreatePeerForElement(
                                            view.button_AutomationTest);
    IInvokeProvider provider
              = (IInvokeProvider)peer.GetPattern(PatternInterface.Invoke);
    provider.Invoke();

    EnqueueCallback(() => Assert.IsTrue(view.ButtonClicked));
    EnqueueTestComplete();
}

Behind the scenes, the AutomationPeer is simply calling an internal method of the ButtonBase named AutomationButtonBaseClick, which raises the button’s Click event.


Note

The ButtonAutomationPeer raises the button’s Click event and not its Tap event. Unfortunately, the Tap event is not supported by the Automation API in the current 7.5 release of the Windows Phone SDK.


Inversion of Control (IoC)

Windows Phone presents some interesting challenges for testing apps in various deployment scenarios. For example, a common requirement for phone apps is the need to behave differently depending on whether the app has been purchased or whether it is in trial mode. Furthermore, the launcher and chooser API does not play well with unit tests because launchers and choosers cause the app to be deactivated. Hence, there is a need for some mechanism to alter the behavior of an app, depending on its deployment scenario, and to decouple and replace various phone-specific types so that code can be more easily tested. One way to achieve these things is by using Inversion of Control.

IoC encompasses two key concepts presented in this chapter: service location and dependency injection.

Service location allows you to associate a type, often an interface, with a concrete implementation of that type so that when another component requests the first type, they are automatically delivered an instance of the associated type. This allows your code to be decoupled from any particular concrete implementation, which increases flexibility, and allows the behavior of your app to be modified, without having to change all references to a particular concrete implementation.

Dependency Injection (DI) assists in the creation of objects by automatically supplying known types to the objects constructor during instantiation.

Employing IoC in Windows Phone apps helps overcome the challenges brought on by the rigid infrastructure and numerous sealed classes that account for many of the key types in the SDK and that hinder both unit testing and ad hoc testing.

Numerous IoC frameworks have been designed for Silverlight for the browser and the desktop CLR. Silverlight for Windows Phone, however, is based on the Microsoft .NET Compact Framework (.NET CF) and lacks the ability to generate Microsoft Intermediate Language (MSIL) and, in particular, Reflection.Emit, a key ingredient in generating types at runtime. This limitation means that none of the well-known IoC projects, such as the Microsoft Patterns and Practices Unity framework, exist for the phone. I have, however, created an IoC container and DI framework, based on work by Ian Randall (http://microioc.codeplex.com/), for use with Windows Phone.

Like much of the code presented in this book, these classes are present in the downloadable sample code. I recommend, however, that you procure the latest code from http://calciumsdk.com, where you are sure to have the most up-to-date version.

A Custom IoC Container and DI Framework

The custom Dependency class is a static class used to associate types or resolve instances of types at runtime. The Dependency class serves as a proxy to an instance of IoC container, allowing you to change the underlying IoC container implementation. It does this by leveraging the Microsoft.Practices.ServiceLocation API, which provides a container agnostic type resolution mechanism. The Dependency class also uses a custom IDependencyRegistrar to create type associations, something notably absent from the Microsoft.Practices.ServiceLocation API.

To use the Dependency class, an IoC container implementation must be specified.

Initialization of the IoC infrastructure should be performed as soon as possible before the app displays its UI. This helps prevent type resolution failures.

In the sample, located in the WindowsPhone7Unleashed.Examples project, the container initialization code is placed in the App class, as shown in the following excerpt:

void InitializeContainer()
{
    SimpleContainer container = new SimpleContainer();
    container.InitializeServiceLocator();

#if DEBUG
    Dependency.Register<ILicensingService>(new MockLicensingService
    {
        IsTrial = true
    });
    Dependency.Register<IMarketplaceDetailTaskAdapter>(
        new MockMarketplaceDetailTaskAdapter(
            () => Debug.WriteLine("Launching Marketplace Detail...")));
#else
    Dependency.Register<ILicensingService, LicensingService>();
    Dependency.Register<IMarketplaceDetailTaskAdapter,
                        MarketplaceDetailTaskAdapter>();
#endif
}

Once the container is initialized, the Dependency class is used to register type associations. Creating a different set of type associations, depending on the build configuration, can be achieved using preprocessor directives. When using a DEBUG build configuration, mock implementations of the ILicensingService interface and the IMarketplaceDetailTaskAdapter interface are used. These types wrap and substitute the functionality provided by the built-in MarketDetailTask and LicenseInformation classes.


Tip

Rather than duplicating your type mappings across test and core projects, you may choose instead to create a dedicated class that includes type associations for each scenario you are covering: test, release, trial, and so on.


If you are familiar with unit testing in Silverlight for the browser, you may wonder why there is no coverage of dynamic mocking in this chapter. The reason is that dynamic mocking does not exist in Windows Phone due to, as previously mentioned, the absence of Reflection.Emit.

Testing Trial Conditions

Providing a trial version of your app may help to improve its monetization. If someone has the opportunity to try out your app, they may be more likely to purchase it (assuming that it is any good). A usual requirement for an app employing a trial version is to provide the user with a link to buy the app within the app itself. In the following demonstration you see how to display an application bar button when an app is in trial mode.

The Microsoft.Phone.MarketPlace.LicenseInformation class contains a single method called IsTrial(), which indicates whether your app has been purchased. When a user downloads the trial version of your app from the marketplace, this method returns true. On all other occasions, such as when debugging, this method returns false.

To ensure that an app functions correctly in both scenarios, you abstract the LicenseInformation class.

Abstracting the LicenseInformation Class

The downloadable sample code includes a custom ILicenseService interface, which contains a single property called Trial, indicating the trial state of the app. There are two implementations of this interface: one for testing and one for production.

The production implementation named LicensingService wraps a LicenseInformation instance, as shown in the following excerpt:

public class LicensingService : ILicensingService
{
    public bool Trial
    {
        get
        {
            LicenseInformation licenseInformation
                                    = new LicenseInformation();
            return licenseInformation.IsTrial();
        }
    }
}

During development and testing, the LicensingService class is replaced by a MockLicensingService class, which allows you to change the value of its Trial property. See the following excerpt:

public class MockLicensingService : ILicensingService
{
    bool trial = true;

    public bool Trial
    {
        get
        {
            return trial;
        }
        set
        {
            trial = value;
        }
    }
}

The particular implementation that is used depends on the type association within the IoC container. When the project is built using a release configuration, then LicensingService is used; otherwise the MockLicensingService is used.

Within the ChatClientView page, there is a button whose visibility depends on a viewmodel property called BuyOptionVisible. See the following excerpt:

bool buyOptionVisible;

public bool BuyOptionVisible
{
    get
    {
        return buyOptionVisible;
    }
    private set
    {
        Assign(() => BuyOptionVisible, ref buyOptionVisible, value);
    }
}

In the viewmodel constructor the value of the buyOptionVisible field is determined using the ILicensingService, which is resolved via the static Dependency method called Resolve:

var licensingService = Dependency.Resolve<ILicensingService>();
buyOptionVisible = licensingService.IsTrial;

An AppBarMenuItem is bound to the BuyOptionVisible property, as shown:

<u:AppBar.MenuItems>
    <u:AppBarMenuItem Text="Buy"
        Command="{Binding BuyCommand}"
        Visibility="{Binding BuyOptionVisible,
        Converter={StaticResource BooleanToVisibilityConverter}}" />
</u:AppBar.MenuItems>

A value converter is used to convert the Boolean value to a Visibility enum value.

When the user taps the button, the BuyCommand is executed. In the next section you see how the BuyCommand uses an abstracted MarketPlaceDetailTask to launch the built-in marketplace application on the phone.

Testing with Launchers and Choosers

Unit testing code that uses a launcher or chooser directly is a challenge because both types of tasks cause your app to be deactivated. No means to abstract launchers or choosers exists out of the box, therefore I have included in the downloadable sample code a set of classes that do just that.

Just as we abstracted the built-in LicenseInformation class in the previous section, here we do the same with the MarketDetailTask. The MarketDetailTask is a launcher which, when shown, takes the user to the built-in marketplace application. When showing the MarketplaceDetailTask during a debugging session, the native marketplace application displays an error because it expects an app with an id that has been officially published to the marketplace.

The main issue, however, is that when the MarketplaceDetailTask is shown, it deactivates the app. Thus, it makes sense to abstract the task as in the LicenseInformation class in the previous section.

The abstracted custom interface for the MarketplaceDetailTask is named IMarketDetailTask and contains a single method named Show. There are two implementations of this interface. The first, named MarketplaceDetailTaskAdapter, calls the Show method of a built-in MarketplaceDetailTask instance when its own Show method is called, as shown in the following excerpt:

public class MarketplaceDetailTaskAdapter : IMarketplaceDetailTask
{
    public void Show()
    {
        var marketplaceDetailTask = new MarketplaceDetailTask();
        marketplaceDetailTask.Show();
    }
}

The unit test compatible implementation of the IMarketplaceDetailTask is called MockMarketplaceDetailTask and allows a specified Action to be invoked when the Show method is called:

public class MockMarketplaceDetailTask : IMarketplaceDetailTask
{
    readonly Action action;

    public MockMarketplaceDetailTask(Action action)
    {
        this.action = action;
    }

    public void Show()
    {
        if (action != null)
        {
            action();
        }
    }
}

The ChatClientViewModel class contains an ICommand named BuyCommand, which, when executed, retrieves the IMarketplaceDetailTask from the IoC container and calls its Show method. BuyCommand is initialized in the viewmodel’s constructor, as shown:

buyCommand = new DelegateCommand(
    obj =>
    {
      var marketplaceDetailTask
              = Dependency.Resolve<IMarketplaceDetailTask>();
      marketplaceDetailTask.Show();
    });

When the BuyCommand is executed, it causes the IMarketDetailTask instance to be resolved using the static Dependency.Resolve method. Recall that a particular implementation of the IMarketDetailTask is registered according to the selected build configuration, as described in the earlier section, “A Custom IoC Container and DI Framework.” If the build is using a Release configuration, then an instance of the MarketplaceDetailTaskAdapter is resolved, which, in turn, causes an instance of the built-in MarketplaceDetailTask to be shown. Conversely, if the build is using a Debug configuration, then the MockMarketplaceDetailTask is used, which does not disrupt unit testing or any manual ad hoc testing.

Summary

This chapter explored the Silverlight Unit Testing Framework (UTF) for Windows Phone. The chapter began with a walk-through of the creation of a unit test project. You saw how to create test classes and test methods and looked at the tag expressions editor, which provides a useful mechanism for selecting which tests are to be run during a test session.

The chapter then examined the UTF in greater detail by creating a simple chat client app. You saw how to test a viewmodel’s behavior before creating a UI and explored code driven UI testing. You also learned how to perform asynchronous testing and touched on the Microsoft Automation framework.

The chapter then examined some advanced topics such as Inversion of Control, mocking, and how to use a custom IoC container.

Finally, you saw how to perform testing of trial applications. You learned how to hide or reveal content based on a mock licensing service and examined a custom API for mocking launchers and choosers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.28.237