![]() |
Platforms, Frameworks & Libraries »
Windows Workflow Foundation »
General
Intermediate
License: The Code Project Open License (CPOL)
Using Workflow Foundation and Visual Studio 2008 for Testing AutomationBy Sergey ArhipenkoHow to employ Windows Workflow Foundation for testing process? Let's design tests on visual diagram and automate its execution! |
C# (C# 3.0), Windows (Win2K, WinXP, Win2003, Vista, TabletPC), .NET (.NET 3.0, .NET 3.5), ASP.NET, Visual Studio (VS2008), WinForms, Architect, Dev, QA, Design
|
||||||||||
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
Download Source Code: Testflow.zip - 172.71 KB
In this article I will show how to employ Windows Workflow Foundation (WF) for testing. Originally, Microsoft aimed WF on describing business processes inside an application. The idea is that we can use this technology to describe the process of testing as well. Tester designs a test using visual diagram and, what is more important, he can execute this test automatically.
After reading the article you will learn:
To demonstrate all the "How-Tos" I created TestflowFramework on C# and attached it to the article. I expect you have had enough knowlege of WF and C# to say "hello world". The rest of stuff I will explain below.
Using TestflowFramework tester can design tests on diagrams in Visual Studio 2008 (for brevity I named “Testflow” a diagram with testing workflow). Blocks on Testflow diagram correspond to actions of user, e.g. ‘type a text in the field’ or ‘click the button’. After diagram is done, tester can run it and all actions will be automatically simulated against the application.
Solution accompanied this article contains sample application and its testflows. The sample application manages users list. It supports viewing/adding/editing/removing users. The sample testflow performs the sequence of following actions: clicks ‘Add User’, enters ‘Login’, ‘Full Name’, ‘Department’ fields and so on. In next chapters I will also tell how to reuse this test and run it with different parameters multiple times.
This article concentrates on testing Windows Forms applications. Similarly, this approach can be extended on ASP.NET. One could even adopt it to Compact Framework and XNA platforms but with some additional efforts.
Well, Windows Workflow Foundation is a very powerful technology and blending with Visual Studio 2008 makes it utterly explosive. Workflow Foundation has perfect extensible architecture both at runtime and design time.
In runtime we need emulate user actions and apply it to Windows Form. Current version of TestflowFramework implements several activities: ClickButtonActivity, InputTextActivity, CloseFormActivity. Anyone can extend this set and add some advanced activities to manipulate grids, viewers, etc.
For design time I developed a designer classes that tune style of WF components, captions and enforce some restrictions. Particularly, any testing action is allowed to be added only into a FormScope activity.
FormScope activity is a container element that hosts any testing activities and supplies context for them. In other words, FormScope contains actions that user can do on given form. After putting FormScope on diagram, tester selects one form existing in application. See picture below:
The picture below shows ClickButtonAction on FormScope bound to AddUserForm. Note that clicked button is being chosen from a strongly typed list that is read from AddUserForm through Reflection.
It’s also worth to mention that the action emulation is done through Reflection. This approach has pros and cons. First of all if you change resolution, coordinates and layout of your form, tests will work well despite everything because tests are bound to programmatic names of forms and controls. And this gives a second advantage: when you rename/delete a control, you will be notified by compiler (yep, at compile time!). The drawback is that you will probably need to develop custom testing activities for some controls. But reward is a high reliability of tests. Notwithstanding, this is not sole approach. Some cases may require another one (for example if you want to test Win32 applications).
Sample contains the application managing a simple user list. It has two forms:
As I has told above, our test is a sequence of user actions nested inside FormScope activity. Every user action is described by dedicated activity type. In given version of Testflowframework I included four types:
1. ClickButtonActivity - this activity clicks specifie button on form
2. InputTextActivity - this activity types specified test into a TextBox control
3. CloseFormActivity - this activity closes current form; it is equivalent to clicking Close button at the top right corner of the window
4. ReadProperty - actually, it does not correspond to user action directly; instead of doing something it allows to read any control property and save it into the internal property of workflow for successive checking (it is detailed below in "Validating Test Results" chapter)
In this test button "Add User" is clicked. Then Login, Full Name and Department fields are filled with specified test data. And click on "OK" button commits it eventually.
Now, if you grasp these basic principles, let's move further.As soon as you create test, you want to reuse it with different parameters. Let’s say we have “AddUser” test from above. How can we create 3 users with different Login, Full Name, and Department? -- We will use technique of binding values with workflow properties to accomplish this task. See two steps below:
1. Create Test Part with parameters.
Create “AddUser” activity. Open its diagram and add an InputTextActivity. For TextBox property of this activity select txtLogin and bind InputText to workflow property “Login”. See picture below:
Do same thing for FullName and Department. Finally, you have to have three activities bound to three properties.
2. Reuse test part and set its parameters.Create new workflow and drag-n-drop “AddUser” test from toolbox. Then set values of properties Login, FullName, Department. You may add this test as many times as you need to create any number of users.
At this point our test has created 3 users. At least it is due... How we can verify this fact and alert if results are wrong?
Well, in given case we will simply validate number of users created: we will access to the list of users (listUsers control at Main Form) and check number of its items. If it is not equal to 3, we abort the test.
I have developed custom activity ReadProperty that can be reused to read any property from any control and make its value available for further checking in any of your tests. In given case, we will read Items.Count property from listUsers control:
As you can see the reading data from application is simple. We should select Control Name located on our form, and then we set the Property Name of this control. Finally, we specify binding for Destination Value. So, the value of listUsers.Items.Count will be saved into the UsersCount property of current workflow class.
On next step we will validate this UsersCount property against a constraint:
Thus, we check the condition. In case of failure we can even specify the reason and it will be written into the log file. On the contrary, if all goes well form is gracefully closed after a short delay.
Current version uses a very simple approach for test starting. To start test you need to start application with several command line parameters, specifying testing assembly containing test and test name itself.
For example:
WinApp.exe -test "TestflowsForWinApp.dll" AddUser_TestFlow TestLog.txt
After test is done, its result is written into log:
31.12.2007 2:01 | Success | AddUsersBatch 31.12.2007 4:18 | Failure | AddUsersBatch. Reason: User Count is Wrong
Log file is named TestLog.txt and stored in application folder (e.g. \WinApp\bin\Debug\ for sample application).
No doubt, this simplified approach can be elaborated for particular requirements. Log files could be saved into xml file. Test launching engine can be implemented in more sophisticated manner. Notwithstanding, I leave it as simple as possible for the sake of highlighting the primary idea.
1. Do not use standard InvokeWorkflow activity to invoke one test from another. This activity starts specified workflow asynchronously. In most cases it is undesired behavior.
2. Put your reusable test parts into separate assembly apart from the assembly with main tests. Assembly with main tests should refer the assembly with test parts. In this case you will see test parts in Toolbox and can easily drag-n-drop them into main tests. If test parts would be in the same assembly, Visual Studio does not show them in Toolbox.
3. You may use any standard activities to enrich your tests. For example, use DelayActivity to emulate user thinking.
Thus, you know how to create a test using TestflowFramework. It is time to learn how design own activity type and enrich your tests with new user actions. Below we will go through the proccess of creation InputTextActivity that allows enter test text into TextBox control:
1. Create Activity class and inherit it from WindowsControlActivity.
public partial class InputTextActivity : WindowsControlActivity
{
}
We could inherit from most general Activity class but WindowsControlActivity provides us with some basic functionality required for testing activity.
2. Declare TextBox property.
According to Workflow Foundation paradigm properties should be declared in a special manner shown below:
public static readonly DependencyProperty TextBoxProperty =
DependencyProperty.Register(
"TextBox",
typeof(string),
typeof(InputTextActivity),
new PropertyMetadata(
"",
DependencyPropertyOptions.Metadata,
new Attribute[]
{ new ValidationOptionAttribute(ValidationOption.Required) }
)
);
[DefaultValue(""),
Description("TextBox where the text will be typed"),
RefreshProperties(RefreshProperties.All),
MergableProperty(false),
TypeConverter(typeof(ControlTypeConverter)),
Category("Testing")]
public string TextBox
{
get
{
return (base.GetValue(TextBoxProperty) as string);
}
set
{
base.SetValue(TextBoxProperty, value);
}
}
You could also note TypeConverter(typeof(ControlTypeConverter)) attribute. This is very important point for extending design time behavior. ControlTypeConverter begins work when our Activity is selected on diagram and user tries to set value of TextBox property in Properties window.
3. Implement type converter.
Type converter provides designer with list of values and user may select one of them from drop down list. ControlTypeConverter is a really simple class that you can see in following code snippet:
protected class ControlTypeConverter : TypeConverter
{
// Methods
public ControlTypeConverter()
{ }
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
IControlFieldProvider provider = null;
object[] instance = context.Instance as object[];
if ((instance != null) && (instance.Length > 0))
{
provider = instance[0] as IControlFieldProvider;
}
else
{
provider = context.Instance as IControlFieldProvider;
}
ICollection values = new object[0];
if (provider != null)
{
values = provider.GetPropertyValues(context);
}
return new TypeConverter.StandardValuesCollection(values);
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{ return true; }
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{ return true; }
}
protected Type filterType = typeof(Control);
ICollection IControlFieldProvider.GetPropertyValues(ITypeDescriptorContext context)
{
StringCollection strings = new StringCollection();
if (this.FormType != null)
{
BindingFlags flags =
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.GetField;
foreach (FieldInfo info in this.FormType.GetFields(flags))
{
if (!info.IsSpecialName)
{
if (info.FieldType == filterType || info.FieldType.IsSubclassOf(filterType))
strings.Add(info.Name);
}
}
}
return strings;
}
Sticking to this pattern you can declare any property with any predefined values.
4. Declare the InputText property.
Now, it time to move to second property: InputText. This property does not have predefined values and may accept any string value. But it has another useful feature: it is bindable. First of all we have to prepare infrastructure for bindable properties. To do that we declare a special property:
public static readonly DependencyProperty ParametersProperty =
DependencyProperty.Register(
"Parameters",
typeof(WorkflowParameterBindingCollection),
typeof(InputTextActivity),
new PropertyMetadata(
DependencyPropertyOptions.Metadata |
DependencyPropertyOptions.ReadOnly
)
);
public WorkflowParameterBindingCollection Parameters
{
get
{
return ((WorkflowParameterBindingCollection)(base.GetValue(ParametersProperty)));
}
}
... and initialize it in constructor with following line:
base.SetReadOnlyPropertyValue(ParametersProperty, new WorkflowParameterBindingCollection(this));
Binding infrastructure is ready. Declare the property:
public static DependencyProperty InputTextProperty =
DependencyProperty.Register(
"InputText",
typeof(string),
typeof(InputTextActivity)
);
[Description("Text entered into the text box")]
[Category("Testing")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string InputText
{
get
{
return ((string)(base.GetValue(InputTextProperty)));
}
set
{
base.SetValue(InputTextProperty, value);
}
}
5. Implement runtime behavior.
Well, this point is very simple. In general case we would override standard Execute method. But it has been done already in parent class WindowsControlActivity. This class performs some preparation routines required for proper working with windows controls during the test. It also defines DoControlActivity method that should be overriden by inheritor. And we do that:
protected override void DoControlActivity(Form form)
{
TextBox box = GetControl(form, TextBox) as TextBox;
box.Select();
box.Text = InputText;
}
Our testing activity selects specified TextBox and "types" text there.
Congrats! We have extended Workflow Foundation and make our test activity work.
We declated a class inherited from WindowsControlActivity (or Activity). Then we declared two properties: first one had a predefined list of controls and second one was bindable string property. All declarations was made by a specific pattern required by WF. Finally, we defined runtime behavior through overriding DoControlActivity method (or Execute method if we need to do something general).
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 7 May 2008 Editor: Genevieve Sovereign |
Copyright 2007 by Sergey Arhipenko Everything else Copyright © CodeProject, 1999-2009 Web22 | Advertise on the Code Project |