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
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
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
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:
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.
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:
- Periodic screen captures or value comparisons.
- Screen captures with value validation at completion.
- 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.
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