Welcome to WindowsClient.net | Sign in | Join

WPF Application Quality Guide

Introducing "WPF Application Quality Guide v.0.2"

In light of the rapid adoption of the WPF and the continuous requests from partners and customers, the WPF team at Microsoft is happy to present the second preliminary release of the “WPF Application Quality Guide”. We plan to release the Guide in stages, updating and fine-tuning the content, based on feedback provided by you. This second release contains more examples that illustrate how to find the AutomationElement object by using UI Automation; introduction to the WPF programming stack and software testing; high level overview of test methodology, planning, and strategies; visual verification testing; media testing; verification of animations and other transitions; performance best practices resources; security testing considerations; a list of tools for creating, debugging, and testing WPF applications; and additional topics in the Appendix about resources for WPF data binding and debugging, and about WPF interoperability with Win32 and Windows Forms.

We appreciate your feedback, so please leave comments below. Specifically, please let us know what you think of the way we have organized the content and the ease of navigating through it.

The WPF Team

Last Update: May 5, 2008

Back to Top INTRODUCTION

This document provides an overview of testing Windows Presentation Foundation (WPF) applications and controls. The document is intended for developers, testers, and vendors who use Microsoft WPF technologies.

Topics that are covered include the following:

  • A basic definition of software testing.
  • Information about the WPF programming stack.
  • Information about different levels of testing, from API testing to integration to system testing.
  • A list of available tools and related references.

Sample code is provided wherever applicable.The content flow of each topic, when applicable, goes from the general concepts or definitions, to development practices highlights, to testing practices, and then to sample code (if any).

EDITIONS OF THIS DOCUMENT

The document will be released in several editions. This is the second edition. Sections that are not yet covered in this document will be included in future editions, as noted in this document.

Please send any feedback to wpftbest@microsoft.com.

Back to Top GETTING STARTED

Back to Top INTRODUCTION TO SOFTWARE TESTING

There are many definitions of software testing. However, all these definitions boil down to essentially the same thing: executing software in a controlled manner in order to evaluate a program or system, and to determine whether it meets its design requirements (customer requirements and specifications, etc).

This section describes high-level concepts for software testing, including techniques, approaches, types, and the testing stack. It also offers some references for additional information about software testing.

Depending on how the testing is managed in a team and on the test type (described below), testing can be performed by testers, by developers, or both. For example, even if a development team includes dedicated testers, unit testing (described below) is performed by developers.

The following are some key concepts for software testing.

Objective

The objective of software testing is to proactively prevent defects and to improve quality.

Techniques

Testing techniques include the following:

  • White box testing. This is testing that is based on knowledge of an application's implementation. Code inspections and code coverage analysis are two examples of white box testing.
  • Black box testing. This is testing that is based solely on what can be observed about an application's interface and behavior, without knowledge of the application's implementation.
  • Grey box testing. This is a mix of white box testing and black box testing, where some knowledge of the application's implementation is used to guide observation-based testing.
Approaches

Software testing can be carried out by using the following approaches:

  • Manual tests. These are typically tests that work best with human eyes, brains, and intuition behind the keyboard.
  • Automated tests. These are tests that can be coded and run without user intervention. They are typically repetitive in nature, and they can be run on a frequent and regular basis, giving constant feedback to the developers about how things are working.
Types

The different levels of testing generally include:

  • Unit testing. Using white box testing, developers carry out unit testing in order to check whether a particular module or unit of code is working properly. Unit testing is implemented at a fundamental level; it is carried out when the unit of the code is developed or when particular functionality is built.
  • Integration testing. This is performed in order to test whether two or more units or modules coordinate properly. Integration testing helps to discover whether there are defects in the interface between different modules.
  • Functional testing. This tests whether a system meets functional requirements. Functional testing does not cover internal coding of the project. Instead, it checks whether the system behaves according to expectations.
  • System testing. This evaluates the system's compliance with its specified requirements, such as performance, compatibility, security, regression, reliability, accessibility, and so on.
  • Acceptance testing. This is performed on a system prior to its delivery, either from the development team to the test team, or from the system provider to the customer (the user or client). In either case, the testing involves running a suite of tests on the completed system to verify basic functionality.

THE TESTING STACK

The design of tests is usually subject to the same basic engineering principles as the design of software. The design consists of stages from high-level strategy to detailed test procedures. The testing stack generally includes the following:

  • Creating a test plan and test specification (test documentation).
  • Designing tests (test cases).
  • Executing tests (manual or automated).
  • Capturing and analyzing test results (logging and failure analysis).
  • Identifying problems (bug isolation and reporting).
  • Having problems fixed and then repeating tests as needed.

Note that complete testing of a product is infeasible. The limiting factor is complexity—that is, the large number of variables that are represented by all the inputs, feature interactions, configurations, code paths, and so on in an application. At some point, software testing has to stop and the product must to be shipped or development must be discontinued. The point at which testing can be suspended is decided by the trade-off between time and budget (or with respect to other team-specific criteria), or if the reliability estimate of the software product meets the stated requirements.

TESTING RESOURCES

The following books provide information about automated testing:

  • Automated Software Testing: Introduction, Management, and Performance by Elfriede Dustin, Jeff Rashka, and John Paul, Addison-Wesley, 1999.
  • Just Enough Software Test Automation by Daniel J. Mosley and Bruce A. Posey, Yourdon Press, 2002.
  • Software Test Automation: Effective Use of Test Execution Tools by Mark Fewster and Dorothy Graham, Addison-Wesley, 1999.
  • Testing Computer Software by Cem Kaner, Hung Quoc Nguyen, and Jack Falk, Wiley, 1999.

The following Microsoft Web sites also provide information about testing:

For unit testing frameworks and tools, you can use Microsoft Visual Studio Team System Test Edition to create, run, and analyze unit tests. Visual Studio also provides code-coverage test framework and graphical UI for test-result analysis. For a list of tools that you can use to create, debug, profile, and test WPF applications, see the Tools section of this document.

Back to Top INTRODUCTION TO THE WPF PROGRAMMING STACK

The section focuses on:

  • WPF architecture.
  • WPF design principles.
  • The core subsystems of WPF.
  • The general development process for a WPF application.
  • Resources for best practices for creating WPF applications.

WPF ARCHITECTURE

WPF provides a very rich and highly integrated UI stack, exposed through a .NET programming model. Figure 1 illustrates the overall architecture and the key components of WPF.

FIGURE 1: ARCHITECTURE AND KEY COMPONENTS OF WPF

The new platform and new tools lead to improved application development workflow, and offer increased productivity, improved ability to implement an application design, and faster time to market.

WPF DESIGN PRINCIPLES

The basic design principles of WPF can be categorizes as follows:

  • Integration. WPF provides a unified programming model that is consistent across controls, graphics, text, and media services, and that enables seamless integration of these elements within a single application. The WPF content model enables any control to host any group of other controls. To help arrange the content either in fixed or flow layout, WPF provides container elements that implement various layout algorithms in a way that is completely independent of the content that they are holding. Furthermore, data binding, templates, triggers, and animation provide visualized content and bring the UIs to life, giving users immediate feedback as they interact with the UI.
  • Vector graphics. WPF takes full advantage of the powerful graphical processing units (GPUs) that are part of modern PC systems. At its heart, the composition engine is vector-based, enabling WPF to scale all output to match the resolution of a specific output device. In situations where hardware rendering cannot be used, software rendering is available as a fallback. In addition, a floating-point logical pixel system and 32-bit ARGB color support provide a rich, high-fidelity visual experience that anticipates future technology needs, such as high-DPI displays.
  • Declarative programming. WPF introduces XAML (eXtensible Application Markup Language), an XML-based language for instantiating and populating nested object hierarchies. The design of XAML enables applications to parse and manipulate UI elements at run time. The XAML/code-behind model is supported both by the designer tool Expression Studio and the developer tool Visual Studio, which enable designers and developers to work collaboratively on application design and development.
  • Easy deployment. With ClickOnce for deployment and with support for both standalone applications and browser-hosted applications, WPF offers the best of both deployment models.

MAJOR SUBSYSTEMS OF WPF

WPF is structured by using subsystems or classes that are defined in different namespaces. These classes have a very deep inheritance hierarchy. Figure 2 shows these important classes and their relationships.

FIGURE 2: CLASSES THAT MAKE UP WPF SUBSYSTEMS

The following list describes the classes that make up the WPF subsystems (links lead to documentation on the MSDN Web site for the individual class):

  • Object. The base class for all .NET Framework classes.
  • DispatcherObject. The base class that handles messages from other objects.
  • DependencyObject (DO). The base class for any object that can support dependency properties (DPs). This class defines the GetValue and SetValue methods that are central to the operation of DPs.
  • Freezable. The base class for objects that have a modifiable state and can be “frozen” into a read-only state for performance purposes.
  • Visual. The base class for all objects that have their own visual representation. This class provides a tree of visual objects, each optionally containing drawing instructions and metadata about how to render those instructions, such as clipping, transformation, and so on. Visual is the entry point to the WPF composition system. It is also the point of connection between two subsystems, the managed API and the unmanaged milcore (the core of the WPF rendering system).
  • UIElement. The base class for all visual objects, which provides support for layout, input, focus, and routed events (collectively referred to as LIFE).
  • ContentElement. A base class that is similar to UIElement, but for content that does not have its own rendering behavior. In order to be rendered in the UI, ContentElement objects must be hosted in an object that derives from Visual.
  • FrameworkElement. A base class that adds support for styles, data binding, resources, and a few common mechanisms for Windows-based controls such as tooltips and shortcut menus.
  • Control. The base class that provides basic elements for GUI development, such as Button, ListBox, and so on. The controls separate the data model (properties), interaction model (commands and events), and display model (templates), which enables developers to completely replace their visual aspect.
  • Shape. The base class for shape elements, such as Ellipse, Polygon, and Rectangle.
  • Panel. base class for all Panel elements, which is used to position and arrange child objects in WPF applications.
  • ContentControl. The base class for controls that can have only one child element. This child element can be anything from a string to a layout panel with a combination of other controls and shapes.
  • ItemsControl. The base class for controls that can be used to present a collection of items, such as the ListBox and TreeView controls.
  • The following table summarizes the most important of these core classes in WPF and their basic functionalities:

GENERAL DEVELOPMENT PROCESS FOR A WPF APPLICATION

For an overview of WPF and a tutorial that shows how to develop a WPF application, see the following articles on the MSDN Web site:

RESOURCES FOR BEST PRACTICES FOR CREATING WPF APPLICATIONS

For information about best practices for designers and for developers, and about design pattern for WPF applications, see the following whitepaper on the Windows Client Web site and on the MSDN blog site:

Back to Top TEST METHODOLOGY, PLANNING, AND STRATEGIES

Many books and resources are available that cover software testing methodologies, planning techniques, and testing strategies. (For a list of resources, see the Testing Resources in the Introduction to Software Testing section in this document.) This section provides a high-level overview of general test methodology, planning, and strategies.

A test strategy is a statement of the overall approach to testing. It identifies the levels of testing to be applied and the methods, techniques, and tools to be used. Forming a sound testing strategy is the first step in designing tests, before you move to developing test plans, specifying test-case design, implementing tests, gathering test execution results, and identifying and fixing bugs.

The following are a few key factors to consider in forming a good test strategy and in conducting WPF applications test planning:

  • In-process versus out-of-process testing. Each approach has different performance and security implications, and so on. You select the one or the mix of both based on your application’s needs.
  • White box testing, black box testing, or a combination of both (control-flow testing, data-flow testing, use-case testing, and so on).
  • Manual tests versus automated testing (when and what tests should be automated).
  • Unit testing, system testing, and so on.
  • Synchronization and timing, and so on.

There are many test strategies. Each has advantages and disadvantages that depend on the overall business needs, to name a few. Some of these test strategies are:

  • Analytical test strategies. These start with analysis as a base. This is an object-guided strategy, looking at requirements, design, and implementation objects in order to determine testing focus. This strategy requires an up-front investment of time.
  • Model-based test strategies. These develop models for how the system should behave. This strategy relies on the ability of the tester to develop good models.
  • Methodical test strategies. These use a relatively informal approach, although it is an orderly and predictable one, to determine where to test. This strategy is suitable for relatively stable systems.
  • Process-oriented test strategies. These follow the standardized test procedures defined by ISO, or use agile, lightweight processes. These strategies are often used for user acceptance testing. They are also suited to being able to respond to late changes, and can be tailored for small teams whose product has low scalability.

A good testing principle is to align the project context, the test mission, the test strategy, and the tactics. The better aligned these factors are, the more effective and efficient the test effort will be.

An effective software testing process is typically a mix of test types, executed through a combination of manual and automated testing. The mix and number of tests is determined by the quality requirements of the application. For example, is the application mission critical? Is time-to-market the most important factor?

For developers, the test-driven development (TDD) methodology can be adapted in unit testing. The benefits of TDD are that because many bugs can be discovered during the early development stage by using unit tests, the software testing phase is considerably shorter, and the cost for fixing bugs is lower. This can appeal to companies or projects that have little or no budget for QA staff. These companies can use TDD to improve code quality before the code ever reaches the testing phase.

The ultimate goal of this guide is to help improve the quality and testability of WPF applications. The benefits of testability include availability, flexibility, maintainability, reliability, usability, changeability, and fault tolerance. The following are high-level guidelines for improving testability of WPF applications:

  • Refactor business logic out of the UI by using the Model/View/ViewModel pattern in both application design and testing. For more information about the Model/View/ViewModel pattern for WPF applications, see John Gossman's blog.
  • Deliver simple tests early by investing in unit testing. This finds bugs and defects early in the product cycle, which results in a much lower cost for fixing them.
  • Automate tests from the bottom up (see Figure 3 below). First develop unit tests, then functional tests, then system tests, and so on. Integration tests can be expensive and time consuming. Therefore, create solid unit tests, functional tests, and system tests before moving on to integration tests.
  • Test the UI and code components separately.
  • Perform integration testing on stable UI and on representative customer scenarios.

FIGURE 3 A BOTTOM-UP MODEL FOR AUTOMATING TESTS.

For more details about different test methodologies, planning processes and various strategies, see the Testing Resources section of the Introduction to Software Testing topic of this document.

Back to Top AUTOMATED TESTING

Back to Top UI TESTING

Back to Top BASIC GUIDELINES FOR MAKING UI AVAILABLE

The ability to uniquely identify and locate any control within the UI provides the basis for automated test applications to operate on that UI. Programmatic access to UI elements requires that all UI elements are labeled, property values are exposed, and appropriate events are raised. For standard WPF controls, most of this work is already done through the AutomationPeer class. Custom controls require additional work to make sure that programmatic access is correctly implemented.

ENABLING PROGRAMMATIC ACCESS TO ALL UI ELEMENTS AND TEXT

User interface (UI) elements should be configured to enable programmatic access. If you are working with a standard WPF control, support for programmatic access is built into the control. If the control is a custom control—a control that has been derived from an existing control or a control that has been derived directly from the Control class—you must check the related AutomationPeer implementation for areas that might need modification. To improve testability, make sure that every control in the application has been assigned an AutomationId value (one of the key Microsoft UI Automation properties) that is unique and language neutral. An AutomationId value that is consistent from build to build makes it easy to identify the control in the visual tree, compared to searching for the control by another method.

ADDING NAMES, HELP TEXT, TITLES, AND DESCRIPTIONS TO UI OBJECTS

Assistive technologies, especially screen readers, use the title to identify the location of the frame, object, or page in the navigation scheme. Therefore, the title must be descriptive. Similarly, for WPF controls, the NameProperty and HelpTextProperty values are important for assistive technology devices and for automated testing. This is especially important for WPF ItemsControl objects (TreeView, ListBox, ComboBox, etc.), because the individual item's AutomationId value might be reused in a different subtree under the shared parent.

When an ItemsControl instance is bound to an XML data source, the assistive tool (such as the Narrator application) uses the ToString method to get the value of each item in the ItemsControl instance. This value is simply the string "System.Xml.XmlElement". To provide a meaningful value to the Narrator application, you can bind the AutomationProperties.Name property to the data source's property that is displayed in the ItemsControl instance.

MAKING SURE THAT PROGRAMMATIC EVENTS ARE TRIGGERED BY ALL UI ACTIVITIES

If a control is derived from a standard control or from the Control class, you must check the related AutomationPeer class for areas that might need modifications. You must also expose related events for the new control as needed. By following these practices, you enable assistive tools to be notified of changes in the UI and to notify the user about these changes. For more information about how to create a custom control with AutomationPeer, see the section Custom Control Authoring and Extensibility Testing later in this document. For more information about how to make UI accessible, see Accessibility Best Practices on the MSDN Web site.

A tool such as UI Spy (UISpy.exe) can also help identify the visual elements in the UI tree, discover their properties and what events they raise, and help you interact with the visual elements. The UI Spy tool is part of the Windows SDK download that is available on the Microsoft Download Web site.

Back to Top DISCOVERY OF UI ELEMENTS

This section focuses on how to use Microsoft UI Automation for Automated Testing. It provides a brief introduction to the UI Automation Object Model, outlines the steps for implementing UI Automation in a WPF application, lists best practices and several different approaches to allocating UI elements, and then provides code examples for these approaches.

THE UI AUTOMATION API OBJECT MODEL

Every UI element, such as a window, a button, and so on, is represented by the AutomationElement derived class in the System.Windows.Automation namespace of the UIAutomationClient assembly. An AutomationElement instance corresponds to a UI element regardless of the underlying UI Framework (WPF or Win32). All Automation elements are part of a tree, in which the root element represents the Windows desktop. Through the AutomationElement.RootElement static property you can obtain a reference to the Windows desktop element and from there find any child UI element.

AutomationElement objects expose control patterns that provide properties and events specific to common control types (such as windows, buttons, check boxes, and so on). Control patterns in turn expose methods that enable clients to obtain additional information about the element and to provide input to the element.

STEPS FOR IMPLEMENTING UI AUTOMATION

The following table lists the steps that are required in order to implement UI Automation in a WPF application.

Steps in implementing UI Automation in a WPF application

Step

Description

Add UI Automation references

You must add the following UI Automation DLLs:

  • UIAutomationClient.dll. Provides access to the UI Automation client APIs.
  • UIAutomationClientSideProvider.dll. Provides the ability to automate Win32 controls and to automate the WPF controls that interact with Win32 features. For more information, see UI Automation Support for Standard Controls.
  • UIAutomationTypes.dll. Provides access to the types that are defined in UI Automation.

Add the System.Windows.Automation namespace

This namespace contains everything that UI Automation clients need in order to use the capabilities of UI Automation, except text handling.

Add the System.Windows.Automation.Text namespace

This namespace contains everything that UI Automation clients need in order to use the text-handling capabilities of UI Automation.

Find controls of interest

Automated test scripts locate UI Automation elements that represent controls in the Automation tree. You can reference UI Automation elements in code in the following ways:

  • Query the UI by using a Condition statement. This is typically where the language-neutral AutomationId value is used.

    Note:

    You can obtain an AutomationIdProperty value by using a tool such as UI Spy UI Spy(UISpy.exe) that can itemize the UI Automation properties of a control.

  • Use the TreeWalker class to traverse the whole UI Automation tree or a subset of it.
  • Track focus.
  • Use screen location, such as the location of the pointer.

For more information, see Obtaining UI Automation Elements on the MSDN Web site.

Obtain control patterns

Control patterns expose common behaviors for functionally similar controls. After automated test scripts locate the controls that require testing, they obtain the control patterns of interest from those controls, such as the InvokePattern control pattern for typical button functionality or the WindowPattern control pattern for window functionality.

For more information, see UI Automation Control Patterns Overview on the MSDN Web site.

Automate the UI

After the control pattern has been obtained, automated test scripts can control any UI from a UI framework by using the information and functionality that is exposed by the UI Automation control patterns.


BEST PRACTICES FOR OBTAINING UI AUTOMATION ELEMENTS

The following are best practices for obtaining UI Automation elements:

  • As a rule, obtain only direct children of the RootElement object. A search for descendants might iterate through hundreds or even thousands of elements, possibly causing a stack overflow. If you are trying to obtain a specific element at a lower level, you should start your search from the application window or from a container at a lower level.
  • To find a known element (identified by its Name property, AutomationId property, or other property or combination of properties), it is easiest to use the FindFirst method. If the element to find is an application window, the starting point of the search can be the RootElement object.
  • To find all elements that meet specific criteria that are related to a known element, use the FindAll method. For example, you can use this method to retrieve list items or menu items from a list or menu, or to identify all controls in a dialog box.
  • If you have no prior knowledge of the applications that your client might be used with, you can construct a subtree of all elements that you are looking for by using the TreeWalker class. Your application might do this in response to a focus-changed event. That is, when an application or control receives input focus, the UI Automation client examines children and perhaps all descendants of the element that has received the focus.
  • After you find the supported patterns for a given UI element, we strongly recommend that you do not call GetSupportedPatterns. Performance can be severely affected because this method calls GetCurrentPattern internally for each existing control pattern. If you can, call GetCurrentPattern only for the patterns that you need.

WAYS OF FINDING UI ELEMENTS BY USING UI AUTOMATION

Developers and testers can use the following ways to find an element by using UI Automation:

  • Search for an element's AutomationId value. Note that the AutomationId value could be reused in the descendants. For more information, see Use the AutomationID property on the MSDN Web site.
  • Search based on the localized control name.
  • Search based on a control type.
  • Search based on a PropertyCondition value.
  • Obtain a control reference in an event handler.
  • Search a ListItem object.
  • Search based on a ClassName property value.
  • In a multi-threaded apartment (MTA) application, create a single-threaded apartment (STA) thread for accessing UI that might appear broken.
  • Use the WPF Dispatcher object to automate the AutomationElement object on the UI thread

If your client application might attempt to find elements in its own user interface, developers and testers must make all UI Automation calls on a separate thread. For more information, see UI Automation Threading Issues.


SAMPLES FOR LOCATING UI ELEMENTS

This section includes examples that show how to locate UI elements. The following code examples are excerpts from the code that is available in the sample test project listed under Custom Control Authoring and Testing Sample later in this document.

Example 1. This example shows how to find a button that has the AutomationID value “button1” in a WPF application, and then click it.

/// <summary>
/// Finds a UI Automation child element by AutomationID.
/// </summary>
/// <param name="automationID">AutomationID of the control, such as "button1".</param>
/// <param name="rootElement">Parent element, such as an application window, or
/// AutomationElement.RootElement object when searching for the application
/// window.</param>
/// <returns>The UI Automation element.</returns>
private AutomationElement FindElementByID(String automationID,
AutomationElement rootElement)
{
if ((automationID == "") || (rootElement == null))
{
throw new ArgumentException("Argument cannot be null or empty.");
}
// Set a property condition that will be used to find the control.
Condition c = new PropertyCondition(
AutomationElement.AutomationIdProperty, automationID,
PropertyConditionFlags.IgnoreCase);

// Find the element.
return rootElement.FindFirst(TreeScope.Element | TreeScope.Children, c);
}

Example 2. This example shows how to find a control by control name.

/// <summary>
/// Finds a UI Automation child element by name.
/// </summary>
/// <param name="controlName">Name of the control, such as "button1".</param>
/// <param name="rootElement">Parent element, such as an application window, or
/// AutomationElement.RootElement object when searching for the application
/// window.</param>
/// <returns>The UI Automation element.</returns>
private AutomationElement FindElementByName(String controlName,
AutomationElement rootElement)
{
if ((controlName == "") || (rootElement == null))
{
throw new ArgumentException("Argument cannot be null or empty.");
}
// Set a property condition that will be used to find the control.
Condition c = new PropertyCondition(
AutomationElement.NameProperty, controlName,
PropertyConditionFlags.IgnoreCase);

// Find the element.
return rootElement.FindFirst(TreeScope.Element | TreeScope.Children, c);
}

Example 3. This example shows how to find a control by control type.

/// <summary>
/// Finds a UI Automation child element by control type.
/// </summary>
/// <param name="controlType">Control type of the control, such as Button.</param>
/// <param name="rootElement">Parent element, such as an application window, or
/// AutomationElement.RootElement when searching for the application window.</param>
/// <returns>The UI Automation element.</returns>
private AutomationElement FindElementByType(ControlType controlType,
AutomationElement rootElement)
{
if ((controlType == null) || (rootElement == null))
{
throw new ArgumentException("Argument cannot be null.");
}

// Set a property condition that will be used to find the control.
Condition c = new PropertyCondition(
AutomationElement.ControlTypeProperty, controlType);

// Find the element.
return rootElement.FindFirst(TreeScope.Element | TreeScope.Children, c);
}

Example 4. This example shows how to find a control based on a control condition, such as all buttons that are enabled.

/// <summary>
/// Finds all enabled buttons in the specified root element.
/// </summary>
/// <param name=quot;rootElement">The parent element.</param>
/// <returns>A collection of elements that meet the conditions.</returns>
AutomationElementCollection FindByMultipleConditions(AutomationElement rootElement)
{
if (rootElement == null)
{
throw new ArgumentException();
}

Condition c = new AndCondition(
new PropertyCondition(AutomationElement.IsEnabledProperty, true),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)
);

// Find all children that match the specified conditions.
return rootElement.FindAll(TreeScope.Children, c);
}

Example 5. This example shows how to find a control from an event. When your application receives a UI Automation event, the source object that is passed to your event handler is an AutomationElement instance.

// Shows how to track the Start button’s Invoke event.
// start is the AutomationElement object that represents the Start button.

// Register an event handler for the InvokedEvent method of the Start button.
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, start,
TreeScope.Element,
new AutomationEventHandler(OnStartInvoke));

// The event handler.
private void OnStartInvoke(object src, AutomationEventArgs e)
{
MessageBox.Show("Start has been invoked");
}

Example 6. This example shows how to find an element from a list item. The example uses the FindAll method to retrieve a specified item from a list. This is faster for WPF controls than using the TreeWalker class.

/// <summary>
/// Retrieves an element in a list by using the FindAll method.
/// </summary>
/// <param name="parent">The list element.</param>
/// <param name="index"> The index of the element to find.</param>
/// <returns>The list item.</returns>
AutomationElement FindListItemByIndex(AutomationElement parent, int index)
{
if (parent == null)
{
throw new ArgumentException();
}

Condition c = new AndCondition(
new PropertyCondition(AutomationElement.IsControlElementProperty, true));

// Find all children that match the specified conditions.
AutomationElementCollection found = parent.FindAll(TreeScope.Children, c);
return found[index];
}

Example 7. This example shows how to find a UI element by class name.

/// <summary>
/// Finds an element by its class name starting from a specific root element.
/// </summary>
/// <param name="root">The root element to start from.</param>
/// <param name="type">The class name of the control type to find.</param>
/// <returns>The list item.</returns>
AutomationElement FindListItemByIndex(AutomationElement root, String type)
{
if ((root == null) || (type == ""))
{
throw new ArgumentException("Argument cannot be null or empty.");
}

Condition c = new AndCondition(
new PropertyCondition(AutomationElement.ClassNameProperty, type));

// Find all children that match the specified conditions.
AutomationElementCollection found = root.FindAll(TreeScope.Children, c);
return found[index];
}

Example 8. Sometimes, it is useful to automate an application from within the same process; basically, the application automates itself. This can be useful because your test has access to internal application state that cannot easily be accessed from another process. However, you must be careful not to block the main UI thread, or the application can hang. Therefore, you must run the UIAutomation code on a separate thread. Examples 8 and 9 show a couple of ways to accomplish this.

Example 8 shows how to create an STA thread to access UI that might appear broken in an MTA application. The most common scenario for this situation is when users are using certain builds of Internet Explorer. In these cases, HTML will appear to have no UIAutomation tree. The exception message is:

Message: UI Automation tree navigation is broken. The parent of one of the descendants exists but the descendant is not the child of the parent.

A problem with Internet Explorer is not the only possible source of this error, but if the application is MTA, testers should try the approach illustrated in the following example. The approach is a general good practice for accessing HTML content, because testers do not have to know what platform or version of Internet Explorer the user has. For more information about threading in the .NET Framework, see Managed Threading on the MSDN Web site.

ParameterizedThreadStart workerThread = new
ParameterizedThreadStart(handleWindowNewThread);
Thread thread = new Thread(workerThread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start(<single object for argument to method>);
thread.Join();
...

private void handleWindowNewThread(object arguments) {...}

Example 9. This example shows how to use the WPF Dispatcher object to create a separate thread to automate an AutomationElement object on the UI thread.

private Dispatcher dispatcher = null;
private delegate void SimpleDelegate();

public void PerformAutomationOperations()
{
dispatcher = Dispatcher.CurrentDispatcher;
Thread automationThread =
new Thread(new ThreadStart(ProcessAutomationElementOnThread));
automationThread.SetApartmentState(System.Threading.ApartmentState.STA);
automationThread.Start();
}

private void ProcessAutomationElementOnThread()
{
AutomationElement element = AutomationElement.FocusedElement;

// Perform operations on the AutomationElement object.
dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new SystemDelegate(ReturnBacktoUiThread));
}

private void ReturnBackToUiThread()
{
Application.Current.MainWindow.Title = "Back on the UI Thread";
}

Back to Top UI EVENTS AND INTERACTION

To be covered in future editions.

Back to Top VISUAL VERIFICATION TESTING

Visual verification testing validates that an application or an application component has been rendered to the screen as expected. Fundamentally, there are two types of visual verification testing: verification based on a master image and analytical visual verification. The methods differ only in how an expected image is obtained.

MASTER IMAGE VERIFICATION

For master-image verification, testers are responsible for obtaining the master images. These master images are generally obtained by performing an initial run of a test case and getting a screen capture. The tester visually examines the screen capture for correctness. If it is correct, it is marked and stored as the master image for that test case. All subsequent runs of the test compare an image obtained during the test to the master image.

This approach is simple, but has the following drawbacks:

  • Master images are generally tied to screen resolution, platform, and window chrome and styling. In theory, testers would have to create master images for all combinations of these variables. However, testers can reduce the number of stored images by creating master images that include only a window’s client area and not the entire set of chrome. In addition, testers can transform the scale of comparison images to match the master image DPI.
  • Depending on the number of tests and the permutations of platform and DPI, the number of master images can become large. Updating master images requires that a tester check the correctness of each master image. A large number of master images can result in a test suite that has high ongoing support cost. In addition, the combined size of the stored master images can require significant storage space, which results in longer test setup times, run times, and cleanup times. Therefore, it is important to limit the size and the number of master images. One way to keep down the size of master images is to capture small regions (not full windows) and to capture integrated content (instead of creating a master image for every primitive).

In general, master image verification will work regardless of how the master images are generated. Testers are not bound by knowledge of the internal workings of a component, because they give final approval to the master image that is used for testing.

ANALYTICAL VISUAL VERIFICATION

A second, more involved method of visual verification is analytical visual verification. In this verification method, the collection of master images is replaced by creating a comparison image on the fly. This created image is assumed to be the visually correct image, and is compared against the captured image in the test. In this verification scheme, testers or developers must provide a way to create the reference image, ideally in a way that is entirely independent of the custom component. Creating the reference image generator is directly tied to the implementation details of the custom component, and different solutions are necessary depending on what is being verified.

In some cases, the analytical verification tool might be a reference image renderer. In other cases, it might be a visual recognition system that discovers and validates elements in the rendered image. For example, it might confirm that a blue rectangle of certain size and position exists in the rendered image, or something similar.

In visual validation of Animation objects, a developer or tester could create an expected-value calculator. The tester would use the calculator to generate a static version of an animated scene at a particular point in the animation, which would serve as the reference image. For verification during a test, the animated scene screen capture should match up with the static screen capture that was analytically created.

Generating the reference image on the fly mitigates problems that are associated with master image verification and with differences in platforms, DPI, and so on. With a more fundamental knowledge of the scene being rendered, testers can create masks to remove parts of scene and allow for additional tolerances in cases where a video adapter (or other hardware) matters. Analytical visual verification is more robust, but incurs the cost of creating the rendering tool for analytical verification.

EXPECTED VERSUS ACTUAL IMAGE COMPARISON

The final step involves comparing two images. The least robust solution would be to directly compare the images and indicate failure if even one pixel is different. A better solution is to allow some level of tolerance when comparing the images. There are numerous approaches to adding tolerance, which depend on the scale and complexity of the required tolerance. In a simple case, a test could simply count the number of pixels that differ and indicate failure if a certain percentage of the pixels is different. Additional levels of complexity could be added by not only counting pixels that differ, but measuring the color difference in the pixels and allowing an acceptable tolerance curve.

A more sophisticated approach yet is to use predefined tolerance profiles. A tolerance profiles is essentially a histogram that is based on pixel differences. For example, a tolerance profile can be expressed as "there should be no more than 10 single-pixel mismatches, no more than 2 double-pixel mismatches, no more than 0 mismatches of 3 or more consecutive pixels." The test passes if the observed differences are below the values set in the tolerance profile. For maximum flexibility and robustness in a visual verification suite, it is useful to provide a way to adjust a comparison tolerance.

Back to Top MEDIA TESTING

Media validation presents unique challenges because of the asynchronous nature of media playback and the rate at which video frames are updated during playback (usually 30 frames per second). This means that a one-minute video clip requires 1800 frames (30 frames per second times 60 seconds). Visually verifying the frame accuracy of video playback through the traditional master-image method, as described in Visual Verification Testing, is infeasible due to the volume of master images and difficulty of capturing a targeted frame in a motion video.

With these challenges in mind, a new verification system was designed to meet the following testing goals:

  • Ability to verify video frame sequence or number.
  • Ability to verify the frame color accuracy.
  • Ability to verify video frame accuracy and completeness.
  • Ability to verify a video frame independently of system DPI and of the size of the video being rendered.
  • Ability to verify the video without the use of master images (that is, to make the video self-verifiable).

One solution is to create a custom video from a sequence of composite frames, which are pictures that are obtained by superimposing fields of a frame. The custom video contains multiple decodable sections in every frame, known as frame parts. In this approach, regardless of which frame is captured during testing, the frame always includes information that can be decoded into frame data, such as the frame number, frame orientation, frame content (to check for completeness and accuracy), and actual color sequence (to check for color accuracy).

Figure4 shows a sample of the composite frame.


FIGURE 4 A COMPOSITE FRAME THAT SHOWS FRAME PARTS

COMPOSITE FRAME PARTS

The composite frame illustrated in Figure 4 contains the following frame parts:

  • Frame-orientation part (middle left). This frame part consists of a two-bit pattern and indicates the orientation of the frame. The order of 1 (black) and 0 (white) indicates that the frame is displaying in an upright position. The order 0-1 would mean that the frame is vertically flipped.
  • Frame-number part to the right of the frame-orientation part). This part represents the current frame number in binary form. The value is a 12-bit pattern indicated by black (1) and white (0) squares. The pattern for each frame is different, based on the frame number offset from the first frame. For example, when the pattern shows WWWWWWWWBWBW, it indicates a binary value of 000000001010 (in decimal, the tenth frame). Having 12 bits of data provides information for a 30 fps video at a maximum duration of 2.27 minutes. (2^12 = 4096 frames, 4096 frames/30 fps = 136 seconds, 136 seconds = 2.27 minutes). Note that it is not usually practical to create a test video that is longer than 30 seconds.
  • Frame-info parts (to the right of the frame-number part). These parts contain human-readable information about the current frame, such as the decoded binary bits of the frame number, the expected color sequence values, and the frame number in decimal form. This is useful during test-failure analysis.
  • Color-palette part (middle right). This section consists of four color squares that represent RGB and alpha channels (the alpha channel enables full or partial opacity verification). The color squares appear in different sequence based on the frame number. During color validation, a pixel of each square is sampled and converted into a color value and then compared to the expected color. Due to data loss during the encoding and decoding process, a tolerance value is required.
  • Frame-content part (four corners of the frame). This part consists of four identical sections that are located at the four corners of the composite frame. They are used to validate frame accuracy by comparing any of the two parts for visual defects. Due to data loss that occurs during the video encoding and decoding process, it is normal that the sections show some level of difference. A tolerance value is used to accommodate these differences.

Using composite frame parts for validation does not require testers to maintain any master images, because each frame of the video contains all the information that is needed to verify that frame, including a color bit for full or partial opacity verification. In addition, validation can be done independently of the resolution and the DPI of the video, because frame-part coordinate calculations are based on a percentage value rather than on absolute values. This methodology also enables the tester to validate video playback speed by comparing it to the expected frame number.

Back to Top VERIFICATION OF ANIMATIONS AND OTHER TRANSITIONS

During the process of adding animations, choices that the developer makes when creating animations can affect how the animations are tested and verified. This section describes some common methods for verifying animations, and it describes design decisions that can make it easier to test animations. For more information about animations, see Animation Overview in the WPF section of the MSDN Web site.

WAYS TO APPLY ANIMATION

There are three ways to animate content in WPF: by using only XAML, by using only code, and by using a hybrid of XAML and code. Both XAML-only and XAML/code approaches rely on creating storyboard structures that are called in response to a trigger. In XAML-only animation, triggers can be specified by using EventTrigger, PropertyTrigger (triggers used inside styles), and DataTrigger attributes. In a XAML/code approach, individual storyboards can be triggered as needed outside a RoutedEvent call by using the BeginStoryboard element or other means. It is also possible to control storyboards interactively.

Code-only animation offers several options for creating and adding animations. Storyboards can be created and applied in a way similar to the XAML/code approach, either by creating triggers through code or by calling the BeginStoryboard or Storyboard.Begin methods directly. In addition to the storyboard method of appending animation, entire interactive clock trees can be created and added directly to DependencyObject/DependencyProperty (DO/DP) pairs. This direct approach is often simpler than the storyboard approach, because the developer can apply the animations as either Animation or Clock objects directly to the DO/DP pair without having to create PropertyPath objects that signify the property to be animated. However, the developer is then in charge of beginning or controlling the interactive aspects of the animation.

The following example shows an animation in XAML.

<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowTitle="Fading Rectangle Example">
<StackPanel Margin="10">
<Rectangle Name="MyRectangle" Width="100" Height="100" Fill="Blue">
<Rectangle.Triggers>
<!-- Animates the rectangle's opacity. -->
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:5"
AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</StackPanel>
</Page>

The following example shows how to create an animation with BeginAnimation and ApplyAnimationClock.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Media.Animation;

namespace Microsoft.Samples.Animation.TimingBehaviors
{
public class AnimationClockExample : Page
{
ScaleTransform myScaleTransform;
public AnimationClockExample()
{
this.WindowTitle = "Opacity Animation Example";
this.Background = Brushes.White;
StackPanel myStackPanel = new StackPanel();
myStackPanel.Margin = new Thickness(20);
// Create a button that with a ScaleTransform object.
// The ScaleTransform object animates when the
// button is clicked.
Button myButton = new Button();
myButton.Margin = new Thickness(50);
myButton.HorizontalAlignment = HorizontalAlignment.Left;
myButton.Content = "Animate";
myScaleTransform = new ScaleTransform(1,1);
myButton.RenderTransform = myScaleTransform;

// Associate an event handler with the
// button's Click event.
myButton.Click += new RoutedEventHandler(myButton_Clicked);
myStackPanel.Children.Add(myButton);
this.Content = myStackPanel;
}
// Create and apply and animation when the button is clicked.
private void myButton_Clicked(object sender, RoutedEventArgs e)
{
// Create a DoubleAnimation instance to animate the
// ScaleTransform object.
DoubleAnimation myAnimation =
new DoubleAnimation(
1, // "From" value
5, // "To" value
new Duration(TimeSpan.FromSeconds(5))
);
myAnimation.AutoReverse = true;

// Create a clock for the animation.
AnimationClock myClock = myAnimation.CreateClock();

// Associate the clock's ScaleX and
// ScaleY properties of the button's
// ScaleTransform object.
myScaleTransform.ApplyAnimationClock(
ScaleTransform.ScaleXProperty, myClock);
myScaleTransform.ApplyAnimationClock(
ScaleTransform.ScaleYProperty, myClock);
}
}
}

WAYS TO TEST ANIMATION

Understanding the options for testing animation can help developers tailor how to apply animations to custom code. Assuming that an animation has already been created and applied, there are three ways to verify that the animation is progressing as specified:

  1. Periodic screen captures or value comparisons.
  2. Screen captures with value validation at completion.
  3. Full-value validation.
PERIODIC SCREEN CAPTURES OR VALUE COMPARISONS

Animation is simply a function that describes change over time. Therefore, the most fundamental verification method is to check for the amount of change over a given time. For visual verification, a sequence of screens can be captured and compared against previous captures to check for change. For value comparison, testers can use values instead of screen captures, checking the value returned either by the property being affected by the animation or by the animation itself.

Checking for change is the simplest animation verification method, but some knowledge of the underlying animation is required in order to make this method robust. Some prerequisites for using this method are:

  • Knowledge of when the animation begins or ends, and an expected duration if the animation is not continuous. Obviously, checking for rate of change at a time when the animation is not actively progressing or has already completed will return no changes. For robust validation, developers who add animation must expose a means of obtaining this information.
  • For periodic-values validation, a means of obtaining either the animation (by calling GetCurrentValue) or the DependencyProperty that is being animated are necessary for retrieving the current value of the animation.
  • Ideally, the current progress of the animation is exposed as a read-only value to assist in validating an animation. But the BeginTime and Duration properties can also be used to determine whether the animation is complete. Exposing these values also enables full-value validation when needed.
SCREEN CAPTURES WITH VALUE VALIDATION AT COMPLETION

In addition to validating based on screen captures, testers can inject an additional validation point to ensure not only that the animation is progressing, but that the animation completes at its expected value. Without fully implementing an expected value calculator, testers can infer the final value for animations if they are familiar with the animation. For the most common animation types (From-To, To, and From-By), testers can determine the final value of the animation. For From-To and To animations, the final value should be equal to the value specified in the To property of the animation. For From-By, the final value should be the By value added directly to the From value. This simple calculation quickly becomes quite complex if AutoReverse or RepeatCount is specified when IsAdditive or IsCumulative is set to true.

For the most common animations (From-To and To), the final value calculation will always be the To value regardless of the values of repeatCount and IsAdditive or IsCumulative. However, setting AutoReverse requires a different calculation. For a From-To animation with AutoReverse set to true, the final value should always be the From value. For a To animation, the final value reverts to the original value of the DependencyProperty before the animation was applied. Obviously, the more complex the animation is that is applied, the more difficult it is to calculate the final value.

For a single validation point at completion, it is often easier to hard-code the expected value (if known) in the test. This saves the trouble of implementing a final value calculator.

As animation tests become more complex, testers require more information about the animation and the object that it is affecting in order to properly validate the animation's progress. In order to calculate the final value at completion, testers need access to nearly all the animation properties, such as From, To, By, AutoReverse, BeginTime, Duration , and possibly IsAdditive or IsCumulative.

For validation by visual screen captures or by final value, testers can obtain the required information from the Animation template that is used for the animation. Each AnimationTimeline object is internally used to create an individual AnimationClock instance, so exposing the actual animation exposes only a template. If the developer grants read access to the animation template, a tester can obtain all the information that is required in order to calculate a final value and to make sure that the animation has occurred.

FULL-VALUE VALIDATION

To perform full-value validation for an animation, testers need either the output of the GetCurrentValue method, or the GetValue value of the DependencyProperty of the object that is being animated. For GetCurrentValue, testers can get the origin value through the GetAnimationBaseValue method of the UIElement or Animatable objects. The destination value for common FromTo, To, and By animations is not used to determine the current value of the animation—it is used only for From animations. To be able to use the GetValue method of DependencyObject, testers need access to the DependencyObject that is being animated.

The internal object that is being animated and the feasibility of exposing the DependencyObject for testing purposes determines which method to use to expose a way to get the animated value. For multiple internal objects, if developers want to limit the exposure of internal objects, the easiest route is to expose the result of the GetCurrentValue method. If the animation occurs on a component that is already exposed, the preferred way is to use the GetValue method.

For full-value validation, testers must get the CurrentProgress value of any animation clocks that need to be tested. The CurrentProgress value will be used to calculate the expected value of an animation. The CurrentProgress value can also be calculated from the properties of the animation template and the current time that has elapsed since the application started, by using a calculation like the following one:

CurrentProgress = (CurrentTime – BeginTime) % Duration

To calculate the expected values for an animation, testers should determine the range of the animation and multiply it by the animation progress. Each animation type has a different calculation function, as shown in the following list:

  • (FromTo) currentValue = From + (To – From) * currentProgress
  • (To) currentValue = DO.BaseValue + (To – DO.BaseValue) * currentProgress
  • (FromBy) currentValue = From + By * currentProgress
  • (By) currentValue = DO.BaseValue + By * currentProgress
  • (From) currentValue = DO.BaseValue - From * (1 – currentProgress)

The CurrentProgress value is generally in the range of 0 to 1. However, if an animation is repeating, this value will increase at each repetition. For example, during the first iteration of the animation, the progress range will be from 1 to 2; in the second iteration (first repetition), from 2 to 3, and so on. For all animation types that do not specify IsCumulative or IsAdditive, testers should assume that the CurrentProgress value is in the range of 0 to 1 when performing the previous calculations. An animation with AutoReverse set to true will progress from 0 to 1, and then back to 0.

Verifying animations that are set with Repeat, AutoReverse, and IsCumulative or IsAdditive values is more complex and is not described here. Combining animations though either ClockGroup or SnapShotAndReplace and Compose HandoffBehavior properties complicates the animation calculation function even further. In general, for composing animations, the order in which the animations are processed matters (the order that they are applied or added to the group or triggers, and so on). The calculated value from each individual animation is passed to the next animation as the updated BaseValue property. For multiple composing or interacting animations, the easiest way to validate is to check for consistent change through periodic screen captures or value validation.


ANIMATION APIS THAT AFFECT PROGRESS

The following animation properties and methods affect the calculation of animation progress:

  • Repeat property. On repeat, the CurrentProgress value increments with the repeat count. During the initial animation, the progress goes from 0 to 1. During the first repeat, the value goes from 1 to 2, and so on. For non-additive or cumulative animations, the CurrentProgress value should be considered as going only from 0 to 1.
  • AutoReverse property. On auto-reverse, the CurrentProgress value returns to the starting point. For example, the progress value goes from 0 to 1 and then back to 0, from 1 to 2 and then back to 1, and so on.
  • Seek, Pause, and Resume methods. The process of seeking performs an update on the CurrentProgress value to the specified point at the next tick. Setting SeekAlignedToLastTick seeks to the specified position as of the current tick. Pause and resume status can be checked by the IsPaused property of an AnimationClock instance.
DEVELOPER AND TESTER RECOMMENDATIONS

Depending on the type of animation validation that is required, developers can tailor the animation element exposure of custom components. For simple validation, testers can obtain periodic screen captures and compare them to detect changes by using screen capture and image comparison tools. For triggered animations within a component, testers can begin the capture process after triggering the animation. For ambient animations, validation can be performed at any point.

For more complicated validation, components must expose more information. In order to enable determination of the final value of an animation, the animation template must be exposed so that testers can obtain the animation property values that are required for calculating a final value. Alternatively, if exposure of the animation template is not feasible, the expected value can be hard-coded within the test.

The most complicated validation scheme requires the most exposure. At a minimum, a tester needs the DependencyObject/DependencyProperty (DO/DP) pair for any animation that is applied, or the current result of GetValue, as well as access to the CurrentProgress of the AnimationClock instance. In order to expose AnimationClock instances, BeginAnimation calls must be replaced with ApplyAnimationClock calls for any DO/DP pairs. For Storyboard objects, Storyboard.CreateClock can be used.

The level of exposure that a developer builds into a component dictates the level of animation validation that a tester can implement. For simple FromTo, To, or By animations, exposing the DO/DP pair (or even just the result of GetValue) and the CurrentProgress value of the animating clock delivers the most value in providing the tester complete freedom to validate animations by any means. The level of animation validation that is required should determine the level of exposure of internal components. However, for the most part, simple periodic screen captures can go a long way in validating that an animation is working properly.

WHAT IF TESTERS DO NOT HAVE ACCESS TO THE ANIMATION?

If an animation does not expose sufficient information to enable in-depth testing, testers can still perform some testing. Periodic screen captures to check for change is still a viable method of determining that an animation was applied and is running. The output from DependencyProperty.GetValue also reflects change during the course of the animation.

TESTING FOR ANIMATIONS THAT INTERACT WITH BASE VALUES AND FILLBEHAVIOR

Another factor to consider during animation testing is the interaction of additional animations that might be applied to the code or component. Developers must remember that a custom component can have additional animations applied to it, and that animations triggered from separate layers compose together. WPF animations are composed in the order they are applied, which can result in things not always working the way end users expect.

For example, with the default fill behavior of the HoldEnd property, any animation applied to a property continues to affect the property after the animation completes. If an internal FromTo animation is applied to the Width property of a custom component whose default FillBehavior value is HoldEnd, at the completion of the animation, an end user cannot change the value of the Width property. Regardless of the base value specified, the To portion of the animation always remains at the held value. For developers, the FillBehavior value can be specified as Stop, which decouples the animation on completion, and results in the value returning to the base value. This is frequently not the desired behavior, especially for non-auto-reversing animations. Therefore, a typical scenario is for the animation to remain at its final value, but not to block changes. This can be accomplished by handling the Completed event. In the handler, the developer replaces the base value with the new value, and then decouples the animation, as shown in the following example:

someAnimation.Completed += delegate
{
yourObject.yourDP = someAnimation.To;
// Calling BeginAnimation with null removes any existing animations.
yourObject.BeginAnimation(yourObject.yourDPProperty,null);
}

Any tests that check this behavior just need to determine whether the animation still applies. For FromTo, FromBy, and To animations, if the animation's FillBehavior property is set to the default value, setting the base value when the animation is completed results in no change to GetValue. For By animations, GetValue always return the animated value plus the By value (essentially, it returns anything other than the base value applied). Validation can then be accomplished with no knowledge of the underlying animation. If the tester can make sure that internal animations do not continue to affect any additional animations, the component will interact well with those additional animations.

TESTING FOR ANIMATIONS THAT INTERACT WITH EXTERNAL TRIGGERS

To check that internal animations interact well with triggered animations, a tester can use a similar testing methodology. If the internal animation removes itself on completion and replaces the final value, any subsequent externally triggered animations can animate the value without trouble. An internal animation applied outside the trigger layer will be calculated before the trigger layer, and will not be preempted by the externally applied trigger.

To check that internal animations interact well with triggered animations, a tester can use a similar testing methodology. If the internal animation removes itself on completion and replaces the final value, any subsequent externally triggered animations can animate the value without trouble. An internal animation applied outside the trigger layer will be calculated before the trigger layer, and will not be preempted by the externally applied trigger.

ANIMATION BEST PRACTICES

For information about best practices for creating animations, see Animation Tips and Tricks on the WPF section of the MSDN Web site.

ANIMATION SAMPLES

The following articles on the MSDN Web site provide samples of WPF animations:

Back to Top GRAPHICS AND 3D CONTENT TESTING

To be covered in future editions.

Back to Top API TESTING AND UNIT TESTING

To be covered in future editions.

Back to Top PERFORMANCE AND SCALABILITY TESTING

Performance testing determines how fast a system performs under a particular workload. It can also serve to validate and verify other attributes of the system, such as scalability, reliability, and resource usage. Scalability testing tests how well an application reacts to increasing and decreasing load.

Note This release of the document provides references to development practices for improving WPF application performance. Performance and scalability testing practices will be covered in a future release.

DEVELOPMENT PRACTICES AND REFERENCES

The following articles on the MSDN Web site provide information about best development practices for WPF:

The following blog posts provide additional information:

TESTING PRACTICES

To be covered in a future release.

Back to Top SECURITY TESTING

Security testing helps to determine how well a system or application is protected against unauthorized access and code or data tampering. Security testing helps to identify software flaws that potentially lead to security violations, and to validate the effectiveness of security measures.

The issues that security testing addresses can be remembered by using the acronym STRIDE: spoofing, tampering, repudiation, informatio