How are you instrumenting your UI's? In the past I've read that people have instrumented their user interfaces, but what I haven't found is examples or tips on how to instrument a UI.
By instrumenting, I mean collecting data regarding usage and performance of the system. A MSDN article on Instrumentation is http://msdn.microsoft.com/en-us/library/x5952w0c.aspx. I would like to capture which buttons users click on, what keyboard shortucts they use, what terms they use to search, etc.
How are you instrumenting your UI?
What format are you storing the instrumentation?
How are you processing the instrumented data?
How are you keeping your UI code clean with this instrumentation logic?
Specifically, I am implementing my UI in WPF, so this will provide extra challenges compared to instrumenting a web-based application. (i.e. need to transfer the instrumented data back to a central location, etc). That said, I feel the technology may provide an easier implementation of instrumentation via concepts like attached properties.
Have you instrumented a WPF application? Do you have any tips on how this can be achieved?
Edit: The following blog post presents an interesting solution: Pixel-In-Gene Blog: Techniques for UI Auditing on WPF apps
Here is an example of how I use a simple events manager to hook on to the UI events and extract key information of the events, such as name and type of UI element, name of event and the parent window's type name. For lists I also extract the selected item.
This solution only listens for clicks of controls derived from ButtonBase (Button, ToggleButton, ...) and selection changes in controls derived from Selector (ListBox, TabControl, ...). It should be easy to extend to other types of UI elements or to achieve a more fine-grained solution. The solution is inspired of Brad Leach's answer.
public class UserInteractionEventsManager
{
public delegate void ButtonClickedHandler(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowName);
public delegate void SelectorSelectedHandler(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowName, object selectedObject);
public event ButtonClickedHandler ButtonClicked;
public event SelectorSelectedHandler SelectorSelected;
public UserInteractionEventsManager()
{
EventManager.RegisterClassHandler(typeof(ButtonBase), ButtonBase.ClickEvent, new RoutedEventHandler(HandleButtonClicked));
EventManager.RegisterClassHandler(typeof(Selector), Selector.SelectionChangedEvent, new RoutedEventHandler(HandleSelectorSelected));
}
#region Handling events
private void HandleSelectorSelected(object sender, RoutedEventArgs e)
{
// Avoid multiple events due to bubbling. Example: A ListBox inside a TabControl will cause both to send the SelectionChangedEvent.
if (sender != e.OriginalSource) return;
var args = e as SelectionChangedEventArgs;
if (args == null || args.AddedItems.Count == 0) return;
var element = sender as FrameworkElement;
if (element == null) return;
string senderName = GetSenderName(element);
string parentWindowName = GetParentWindowTypeName(sender);
DateTime time = DateTime.Now;
string eventName = e.RoutedEvent.Name;
string senderTypeName = sender.GetType().Name;
string selectedItemText = args.AddedItems.Count > 0 ? args.AddedItems[0].ToString() : "<no selected items>";
if (SelectorSelected != null)
SelectorSelected(time, eventName, senderName, senderTypeName, parentWindowName, selectedItemText);
}
private void HandleButtonClicked(object sender, RoutedEventArgs e)
{
var element = sender as FrameworkElement;
if (element == null) return;
string parentWindowName = GetParentWindowTypeName(sender);
DateTime time = DateTime.Now;
string eventName = e.RoutedEvent.Name;
string senderTypeName = sender.GetType().Name;
string senderName = GetSenderName(element);
if (ButtonClicked != null)
ButtonClicked(time, eventName, senderName, senderTypeName, parentWindowName);
}
#endregion
#region Private helpers
private static string GetSenderName(FrameworkElement element)
{
return !String.IsNullOrEmpty(element.Name) ? element.Name : "<no item name>";
}
private static string GetParentWindowTypeName(object sender)
{
var parent = FindParent<Window>(sender as DependencyObject);
return parent != null ? parent.GetType().Name : "<no parent>";
}
private static T FindParent<T>(DependencyObject item) where T : class
{
if (item == null)
return default(T);
if (item is T)
return item as T;
DependencyObject parent = VisualTreeHelper.GetParent(item);
if (parent == null)
return default(T);
return FindParent<T>(parent);
}
#endregion
}
And to do the actual logging, I use log4net and created a separate logger named 'Interaction' to log user interaction. The class 'Log' here is simply my own static wrapper for log4net.
/// <summary>
/// The user interaction logger uses <see cref="UserInteractionEventsManager"/> to listen for events on GUI elements, such as buttons, list boxes, tab controls etc.
/// The events are then logged in a readable format using Log.Interaction.Info().
/// </summary>
public class UserInteractionLogger
{
private readonly UserInteractionEventsManager _events;
private bool _started;
/// <summary>
/// Create a user interaction logger. Remember to Start() it.
/// </summary>
public UserInteractionLogger()
{
_events = new UserInteractionEventsManager();
}
/// <summary>
/// Start logging user interaction events.
/// </summary>
public void Start()
{
if (_started) return;
_events.ButtonClicked += ButtonClicked;
_events.SelectorSelected += SelectorSelected;
_started = true;
}
/// <summary>
/// Stop logging user interaction events.
/// </summary>
public void Stop()
{
if (!_started) return;
_events.ButtonClicked -= ButtonClicked;
_events.SelectorSelected -= SelectorSelected;
_started = false;
}
private static void SelectorSelected(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowTypeName, object selectedObject)
{
Log.Interaction.Info("{0}.{1} by {2} in {3}. Selected: {4}", senderTypeName, eventName, senderName, parentWindowTypeName, selectedObject);
}
private static void ButtonClicked(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowTypeName)
{
Log.Interaction.Info("{0}.{1} by {2} in {3}", senderTypeName, eventName, senderName, parentWindowTypeName);
}
}
The output would then look something like this, omitting non-relevant log entries.
04/13 08:38:37.069 INFO Iact ToggleButton.Click by AnalysisButton in MyMainWindow
04/13 08:38:38.493 INFO Iact ListBox.SelectionChanged by ListView in MyMainWindow. Selected: Andreas Larsen
04/13 08:38:44.587 INFO Iact Button.Click by EditEntryButton in MyMainWindow
04/13 08:38:46.068 INFO Iact Button.Click by OkButton in EditEntryDialog
04/13 08:38:47.395 INFO Iact ToggleButton.Click by ExitButton in MyMainWindow
The following blog post gives quite a few good ideas for instrumenting a WPF application:
Techniques for UI Auditing on WPF apps.
You could consider log4net. It is a robust logging framework that exists in a single DLL. It is also done in a "non demanding" type mode so that if a critical process is going on, it won't log until resources are freed up a bit more.
You could easily setup a bunch of INFO level loggers and track all the user interaction you needed, and it wouldn't take a bug crash to send the file to yourself. You could also then log all your ERROR and FATAL code to seperate file that could easily be mailed to you for processing.
If you make use of WPF commands, each custom command could then log the Action taken. You can also log the way the command was initiated.
Perhaps the Microsoft UI Automation for WPF can help out ? Its a framework for automating your UI, perhaps it can be used to log stuff for you...
We use the Automation Framework for auto-testing our UI in WPF.
I have not yet developed using WPF.. But I would assume that its the same as most other applications in that you want to keep the UI code as light as possible.. A number of design patterns may be used in this such as the obvious MVC and Façade. I personally always try and keep the objects travelling between the UI and BL layers as light as possible, keeping them to primitives if I can.
This then helps me focus on improving the UI layer without the concerns of anything going on once I throw my (primitive) data back..
I hope I understood your question correctly, and sorry I cannot offer more contextual help with WPF.
Disclaimer: I work for the company that sells this product, not only that but I am a developer on this particular product :) .
If you are interested in a commercial product to provide this then Runtime Intelligence (a functional add on to Dotfuscator ) that injects usage tracking functionality into your .NET applications is available. We provide not only the actual implementation of the tracking functionality but the data collection, processing and reporting functionality as well.
There was recently a discussion on the Business of Software forum on this topic that I also posted in located here: http://discuss.joelonsoftware.com/default.asp?biz.5.680205.26 .
For a high level overview of our stuff see here: http://www.preemptive.com/runtime-intelligence-services.html .
In addition I am currently working on writing up some more technically oriented documentation as we realize that is an area we could definitely improve, please let me know if anyone is interested in being notified when I have completed it.
Related
See the next post. This original one question content has been removed, as doesn't have any sense. Briefly, I asked how to bind XML (which I generated by mistake while parsing DLL assembly) to TreeView using XmlDataProvider in MVVM way. But later I understood that this approach was wrong, and I switched to generation of data entity model (just write classes which represent all the entities I would like to expose in the tree) instead of XML.
So, the result in the next post. Currently from time to time I update this "article", so F5, and
Enjoy reading!
Introduction
The right way I had found reading this article
It's a long story, most of you just can skip it :) But those, who want to understand the problem and solution, must read this all !
I'm QA, and some time ago had become responsible for Automation of the product I clicks. Fortunately, this automaton takes place not in some Testing Tool, but in Visual Studio, so it is maximally close to development.
For our automation we use a framework which consist of MbUnit (Gallio as runner) and of MINT (addition to MbUnit, which is written by the customer we work with). MbUnit gives us Test Fixtures and Tests, and MINT adds additional smaller layer -- Actions inside tests. Example. Fixture is called 'FilteringFixture'. It consist of amount of tests like 'TestingFilteringById', or 'TestingFilteringWithSpecialChars', etc. Each test consist of actions, which are atomic unit of our test. Example of actions are - 'Open app (parameter)', 'OpenFilterDialog', etc.
We already have a lot of tests, which contain a lot of actions, it's a mess. They use internal API of the product we QA. Also, we start investigation a new Automation approach - UI automation via Microsoft UI Automation (sorry for tautology). So the necessity of some "exporter", or "reporter" tool became severe for managers.
Some time ago I have got a task to develop some application, which can parse a DLL (which contains all the fixtures, tests and actions), and export its structure in the human readable format (TXT, HTML, CSV, XML, any other). But, right after that, I went to vacation (2 weeks).
It happens so, that my girlfriend went to her family until vacation (she also got it), and I remained at home so alone. Thinking what me to do all this time (2 weeks), I remember about that "write exporter tool task" and how long I have been planning to start learning WPF. So, I decided to make my task during vacation, and also dress a application to WPF. At that time I heard something about MVVM, and I decided to implement it using pure MVVM.
DLL which can parse DLL with fixrtures etc had been written rather fast (~1-2 days). After that I had started with WPF, and this article will show you how it ended.
I have spent a major part of my vacation (almost 8 days!), trying to sorted it out in my head and code, and finally, it is done (almost). My girlfriend would not believe what I was doing all this time, but I have a proof!
Sharing my solution step by step in pseudo code, to help others avoid similar problems. This answer is more looks like tutorial =) (Really?). If you are interested what were the most complicated things while learning WPF from scratch, I would say -- make it all really MVVM and f*g TreeView binding!
If you want an archived file with solution, I can give it a bit later, just when I have made a decision, that it is worth of that. One limitation, I'm not sure I may share the MINT.dll, which brings Actions, as it has been developed by the customer of our company. But I can just remove it, and share the application, which can display information about Fixtures and Tests only, but not about actions.
Boastful words. With just a little C# / WinForms / HTML background and no practice I have been able to implement this version of the application in almost 1 week (and write this article). So, impossible is possible! Just take a vacation like me, and spend it to WPF learning!
Step by step tutorial (w/o attached files yet)
Short repetition of the task:
Some time ago I have got a task to develop an application, which can parse a DLL (which contains test fixtures, test methods and actions - units of our unit testing based automation framework), and export its structure in the human readable format (TXT, HTML, CSV, XML, any other). I decided to implement it using WPF and pure MVVM (both were absolutely new things for me). The 2 most difficult problems for me became MVVM approach itself, and then MVVM binding to TreeView control. I skip the part about MVVM division, it's a theme for separate article. The steps below are about binding to TreeView in MVVM way.
Not so important: Create DLL which can open DLL with unit tests and finds fixtures, test methods and actions (more smaller level of unit test, written in our company) using reflection. If you are interested in how it had been done, look here: Parsing function / method content using Reflection
DLL: Separated classes are created for both fixtures, tests and actions (data model, entity model?).We'll use them for binding. You should think by yourself, what will be an entity model for your tree. Main idea - each level of tree should be exposed by appropriate class, with those properties, which help you to represent the model in the tree (and, ideally, will take right place in your MVVM, as model or part of the model). In my case, I was interested in entity name, list of children and ordinal number. Ordinal number is a number, which represents order of an entity in the code inside DLL. It helps me show ordinal number in the TreeView, still not sure it's right approach, but it works!
public class MintFixutre : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
private readonly List<MintTest> _tests = new List<MintTest>();
public MintFixutre(string fixtureName, int ordinalNumber)
{
_name = fixtureName;
if (ordinalNumber <= 0)
throw new ArgumentException("Ordinal number must begin from 1");
_ordinalNumber = ordinalNumber;
}
public List<MintTest> Tests
{
get { return _tests; }
}
public string Name { get { return _name; }}
public bool IsParent { get { return true; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
public class MintTest : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
private readonly List<MintAction> _actions = new List<MintAction>();
public MintTest(string testName, int ordinalNumber)
{
if (string.IsNullOrWhiteSpace(testName))
throw new ArgumentException("Test name cannot be null or space filled");
_name = testName;
if (ordinalNumber <= 0)
throw new ArgumentException("OrdinalNumber must begin from 1");
_ordinalNumber = ordinalNumber;
}
public List<MintAction> Actions
{
get { return _actions; }
}
public string Name { get { return _name; } }
public bool IsParent { get { return true; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
public class MintAction : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
public MintAction(string actionName, int ordinalNumber)
{
_name = actionName;
if (ordinalNumber <= 0)
throw new ArgumentException("Ordinal numbers must begins from 1");
_ordinalNumber = ordinalNumber;
}
public string Name { get { return _name; } }
public bool IsParent { get { return false; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
BTW, I also created an interface below, which implement all the entities. Such interface can help you in the future. Still not sure, should I was also add there Childrens property of List<IMintEntity> type, or something like that?
public interface IMintEntity
{
string Name { get; }
bool IsParent { get; }
int OrdinalNumber { get; }
}
DLL - building data model: DLL has a method which opens DLL with unit tests and enumerating data. During enumeration, it builds a data model like below. Real method example is given, reflection core + Mono.Reflection.dll are used, don't be confused with complexity. All that you need - look how the method fills _fixtures list with entities.
private void ParseDllToEntityModel()
{
_fixutres = new List<MintFixutre>();
// enumerating Fixtures
int f = 1;
foreach (Type fixture in AssemblyTests.GetTypes().Where(t => t.GetCustomAttributes(typeof(TestFixtureAttribute), false).Length > 0))
{
var tempFixture = new MintFixutre(fixture.Name, f);
// enumerating Test Methods
int t = 1;
foreach (var testMethod in fixture.GetMethods().Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Length > 0))
{
// filtering Actions
var instructions = testMethod.GetInstructions().Where(
i => i.OpCode.Name.Equals("newobj") && ((ConstructorInfo)i.Operand).DeclaringType.IsSubclassOf(typeof(BaseAction))).ToList();
var tempTest = new MintTest(testMethod.Name, t);
// enumerating Actions
for ( int a = 1; a <= instructions.Count; a++ )
{
Instruction action = instructions[a-1];
string actionName = (action.Operand as ConstructorInfo).DeclaringType.Name;
var tempAction = new MintAction(actionName, a);
tempTest.Actions.Add(tempAction);
}
tempFixture.Tests.Add(tempTest);
t++;
}
_fixutres.Add(tempFixture);
f++;
}
}
DLL: Public property Fixtures of the List<MintFixutre> type is created to return just created data model ( List of Fixtures, which contain lists of tests, which contains lists of Actions ). This will be our binding source for TreeView.
public List<MintFixutre> Fixtures
{
get { return _fixtures; }
}
ViewModel of MainWindow (with TreeView inside): Contains object / class from DLL which can parse unit tests DLLs. Also exposes Fixtures public property from the DLL of List<MintFixutre> type. We will bind to it from XAML of MainWindow. Something like that (simplified):
var _exporter = MySuperDllReaderExporterClass ();
// public property of ViewModel for TreeView, which returns property from #4
public List<MintFixture> Fixtures { get { return _exporter.Fixtures; }}
// Initializing exporter class, ParseDllToEntityModel() is called inside getter
// (from step #3). Cool, we have entity model for binding.
_exporter.PathToDll = #"open file dialog can help";
// Notifying all those how are bound to the Fixtures property, there are work for them, TreeView, are u listening?
// will be faced later in this article, anticipating events
OnPropertyChanged("Fixtures");
XAML of MainWindow - Setup data templates: Inside a Grid, which contains TreeView, we create <Grid.Resources> section, which contains a set of templates for our TreeViewItems. HierarchicalDataTemplate (Fixtures and Tests) is used for those who have child items, and DataTemplate is used for "leaf" items (Actions). For each template, we specify which its Content (text, TreeViewItem image, etc.), ItemsSource (in case of this item has children, e.g. for Fixtures it is {Binding Path=Tests}), and ItemTemplate (again, only in case this item has children, here we set linkage between templates - FixtureTemplate uses TestTemplate for its children, TestTemplate uses ActionTemplate for its children, Action template does not use anything, it is a leaf!). IMPORTANT: Don't forget, that in order to "link" "one" template to "another", the "another" template must be defined in XAML above the "one"! (just enumerating my own blunders :) )
XAML - TreeView linkage: We setup TreeView with: linking with data model from ViewModel (remember public property?) and with just prepared templates, which represent content, appearance, data sources and nesting of tree items! One more important note. Don't define your ViewModel as "static" resource inside XAML, like <Window.Resources><MyViewModel x:Key="DontUseMeForThat" /></Window.Resources>. If you do so, then you won't be able to notify it on property changed. Why? Static resource is static resource, it initializes ones, and after that remains immutable. I might be wrong here, but it was one of my blunders. So for TreeView use ItemsSource="{Binding Fixtures}" instead of ItemsSource="{StaticResource myStaticViewModel}"
ViewModel - ViewModelBase - Property Changed: Almost all. Stop! When user opens an application, then initially TreeView is empty of course, as user hasn't opened any DLL yet! We must wait until user opens a DLL, and only then perform binding. It is done via OnPropertyChanged event. To make life easier, all my ViewModels are inherited from ViewModelBase, which right exposes this functionality to all my ViewModel.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, args);
}
}
XAML - OnPropertyChanged and commanding. User clicks a button to opens DLL which contains unit tests data. As we using MVVM, then click is handled via commanding. At the end of the OpenDllExecuted handler OnPropertyChanged("Fixtures") is executed, notifying the Tree, that the property, to which it is bind to has been changed, and that now is time to refresh itself. RelayCommand helper class can be taken for example from there). BTW, as I know, there are some helper libraries and toolkits exist Something like that happens in the XAML:
And ViewModel - Commanding
private ICommand _openDllCommand;
//...
public ICommand OpenDllCommand
{
get { return _openDllCommand ?? (_openDllCommand = new RelayCommand(OpenDllExecuted, OpenDllCanExecute)); }
}
//...
// decides, when the <OpenDll> button is enabled or not
private bool OpenDllCanExecute(object obj)
{
return true; // always true for Open DLL button
}
//...
// in fact, handler
private void OpenDllExecuted(object obj)
{
var openDlg = new OpenFileDialog { ... };
_pathToDll = openDlg.FileName;
_exporter.PathToDll = _pathToDll;
// Notifying TreeView via binding that the property <Fixtures> has been changed,
// thereby forcing the tree to refresh itself
OnPropertyChanged("Fixtures");
}
Final UI (but not final for me, a lot of things should be done!). Extended WPF toolkit was used somewhere: http://wpftoolkit.codeplex.com/
I'm writing a small business application in C#, .NET 4.0. I am using SQL Server CE 4.0 as my database.
I use Entity Framework to communicate in both directions with database. My datagridviews are bound to Entity Framework collection, thus user can add new or modify existing data directly in datagridview. The problem is that sorting with Entity Framework bound to datagridview is not really supported. From what I have learned:
I can intercept clicks to column header cells and then perform sorting and rebind result to datagridview. It is a bit tedious, but it works for master datagridview. But when I do that also for "detail" datagridview" then I loose the automatic rebinding of "detail" datagridviews (when new row from master table is selected). So I have to deal with that also.
I can cast query to a list / binding list and pass it to a sortable bindinglist. Well here I have the same problem with rebinding "detail" datagridviews manually. The new problem that here arises is, that now I have to somehow fix saving, because new data is added only to the sortable bindinglist and not directly to the Entity Framework context.
What should I do (and how)? Should I just use DataSets?
My preference is to use an intermediate [observable]collection which holds the sorted entities. However this is in the WPF/MVVM world. And even then the Pattern is still relatively the same for ASP.NET ObjectDataSource or MVC using Collections. It's been quite some time but maybe I can brain dump a bit where hopefully you can find something useful.
I'm pulling this from memory so bare in mind this is nothing more to help point you in some direction.
Form variables we'll use.
private string SortProperty { get; set; }
private ListSortDirection SortDirection { get; set; }
private ICollection<myItems> items; // Entity Collection
private ObservableCollection<myItems> SortedItems { get; set; } // Sorted Collection
Overload the form OnLoad event handler to register a header clicked handler to apply sorting.
protected override void OnLoad(EventArgs e)
{
dataGridView1.ColumnHeaderMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(dataGridView1_ColumnHeaderMouseClick);
LoadDataGridView();
base.OnLoad(e);
}
protected override void OnUnload(EventArgs e)
{
dataGridView1.ColumnHeaderMouseClick -= new System.Windows.Forms.DataGridViewCellMouseEventHandler(dataGridView1_ColumnHeaderMouseClick);
base.OnUnload(e);
}
Perform our initial load of the sorted data.
private void LoadDataGridView()
{
items = myRepository.GetAllItems(); // However you get or have your collection of items.
ApplySort();
dataGridView1.DataSource = SortedItems;
}
Sort our data and save in new collection. The OrderBy requires Dynamic Query Library: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx.
private void ApplySort()
{
// IQueryable<myItems>, ICollection<myItems>, ObservableCollection<myItems>... be aware of cross threading and how you will handle updates.
SortedItems = items.AsQuerable().OrderBy(SortProperty + (SortDirection == ListSortDirection.Ascending ? " asc" : " desc")).ToList();
}
Our click event handler. Remember, you will have to handle entities added, deleted and changed.
private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
var propertyName = GetPropertyName(dataGridView1.Columns[e.ColumnIndex])
SortDirection = SortProperty == propertyName ?
SortDirection == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending
: SortDirection;
SortProperty = propertyName;
ApplySort();
dataGridView1.DataSource = SortedItems;
}
Trivial helper method for sorting.
private string GetPropertyName(int columnNumber)
{
switch(columnNumber)
{
case 0:
return "Id";
case 1:
return "Name";
default:
return "Id";
}
}
Here is some additional information:
http://msdn.microsoft.com/en-us/library/ms171608.aspx
I know this isn't an exact answer but since no one else will chime in, maybe you can take this, hack it up until it works gracefully and then comment your solution for the next person.
In MVVM it is normal to connect View to the ViewModel with data binding.
Therefore if the name of a properties changes on one of the Model objects that is databound to there is no compiler error.
When the compiler will not stop a bug, the next thing I think of is “UnitTest”, However
How do you unit test this without
spending forever writing a GUI test?
Is there a system that will check that all the properties that are bound to is valid, (without having to run the UI) that I can call in a unit test?
I am looking for something that will take the view, and then loop over all WPF controls, for each WPF control it will look at all the binding and check if they are valid.
By the way there have been a few good questions about how to make OnPropertyChanged safe, and/or how to test it (But done of these get down to the level of a WPF view.)
How to make Databinding type safe and support refactoring
Automatically INotifyPropertyChanged
workarounds for nameof() operator in C#: typesafe databinding
A Fluent Interface For Testing INotifyPropertyChanged
Automatic Class Tester will test all simple proptites and INotifyPropertyChanged
I have put a bounty on this question, as someone must have thought hard about the problem and come up with soltions.
I think I've come up with something that may work using simple reflection, and adapting some code I've used in the past (the code for the FindBindingsRecursively method was written by Martin Bennedik's as part of his Enterprise WPF Validation Control):
[TestMethod]
public void CheckWpfBindingsAreValid()
{
// instansiate the xaml view and set DataContext
var yourView = new YourView();
yourView.DataContext = YourViewModel;
FindBindingsRecursively(yourView,
delegate(FrameworkElement element, Binding binding, DependencyProperty dp)
{
var type = yourView.DataContext.GetType();
// check that each part of binding valid via reflection
foreach (string prop in binding.Path.Path.Split('.'))
{
PropertyInfo info = type.GetProperty(prop);
Assert.IsNotNull(info);
type = info.PropertyType;
}
});
}
private delegate void FoundBindingCallbackDelegate(FrameworkElement element, Binding binding, DependencyProperty dp);
private void FindBindingsRecursively(DependencyObject element, FoundBindingCallbackDelegate callbackDelegate)
{
// See if we should display the errors on this element
MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Static |
BindingFlags.Public |
BindingFlags.FlattenHierarchy);
foreach (MemberInfo member in members)
{
DependencyProperty dp = null;
// Check to see if the field or property we were given is a dependency property
if (member.MemberType == MemberTypes.Field)
{
FieldInfo field = (FieldInfo)member;
if (typeof(DependencyProperty).IsAssignableFrom(field.FieldType))
{
dp = (DependencyProperty)field.GetValue(element);
}
}
else if (member.MemberType == MemberTypes.Property)
{
PropertyInfo prop = (PropertyInfo)member;
if (typeof(DependencyProperty).IsAssignableFrom(prop.PropertyType))
{
dp = (DependencyProperty)prop.GetValue(element, null);
}
}
if (dp != null)
{
// Awesome, we have a dependency property. does it have a binding? If yes, is it bound to the property we're interested in?
Binding bb = BindingOperations.GetBinding(element, dp);
if (bb != null)
{
// This element has a DependencyProperty that we know of that is bound to the property we're interested in.
// Now we just tell the callback and the caller will handle it.
if (element is FrameworkElement)
{
callbackDelegate((FrameworkElement)element, bb, dp);
}
}
}
}
// Now, recurse through any child elements
if (element is FrameworkElement || element is FrameworkContentElement)
{
foreach (object childElement in LogicalTreeHelper.GetChildren(element))
{
if (childElement is DependencyObject)
{
FindBindingsRecursively((DependencyObject)childElement, callbackDelegate);
}
}
}
}
Really good question. Voted it up. I would like to know the answer too.
One of the best practices I know (suggested by Josh Smith, thanks Gishu for pointing to this) is having base view model class to check in the OnPropertyChanged() method whether property really exists. E.g.:
abstract class ViewModelBase
{
[Conditional("DEBUG")]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
if (this.ThrowOnInvalidPropertyName)
throw new ArgumentException(propertyName);
string msg = "Invalid property name: " + propertyName;
Debug.Fail(msg);
}
}
protected void OnPropertyChanged(string propertyName)
{
VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
But this wouldn't help you to find spelling problems in XAML. Hmm... I don't know any existing solution for this. Maybe guys from WPF Disciples could suggest something. I think I would go with PresentationTraceSources.DataBindingSource and add to his Listners collection instance of TextWriterTraceListener and then monitor output. As soon as we get an error or warning on our radar we should fail a test.
Found good example: WPF Snippet - Detecting Binding Errors
Hope this helps. At least a bit :).
Cheers, Anvaka.
I know that this is not the direct answer to your question.
If you know the name of the control element, that you expect to be bound against you can do something like the test below (using nunit). This is the rough version. But here you use expressions and explicitly test that the property is in a binding
[Test]
public void TestBindings()
{
TestBinding<IndividualSolutionViewModel, string>(x => x.Name, "Name", TextBlock.TextProperty);
}
private void TestBinding<TViewModel,TResult>(Expression<Func<TViewModel, TResult>> property, string elementName,
DependencyProperty dependencyProperty)
{
string memberName = ExpressionHelper.GetPropertyName(property); // f.ex v => v.Name will return Name
TestBinding(memberName, elementName, dependencyProperty);
}
private void TestBinding(string memberName, string elementInControlName, DependencyProperty dependencyProperty)
{
//object viewModel = new IndividualSolutionViewModel();
var view = new IndividualSolutionView();
//Assert.That(view.DataContext, Is.EqualTo(viewModel));
var element = view.FindName(elementInControlName);
Assert.That(element, Is.Not.Null, string.Format("Unable to find the element {0} in view {1}", elementInControlName, view.Name));
Assert.That(element, Is.InstanceOf(typeof(DependencyObject)));
var binding = BindingOperations.GetBinding(element as DependencyObject, dependencyProperty);
Assert.That(binding, Is.Not.Null, string.Format("Could not find a binding for the control {0}", elementInControlName));
Assert.That(binding.Path.Path, Is.EqualTo(memberName));
}
Ps. You have to add this to the app.config
<configSections>
<sectionGroup name="NUnit">
<section type="System.Configuration.NameValueSectionHandler"
name="TestRunner"></section>
</sectionGroup>
</configSections>
<NUnit>
<TestRunner>
<add value="STA" key="ApartmentState"></add>
</TestRunner>
</NUnit>
There is also this possibility, that might give you some ideas. THe gist of the idea is property names that you would be binding to are exposed as static string properties. If a binding property name changed you would get a compilation error.
I have not had the opportunity to actually test this technique myself - but it does look interesting:
http://www.codeproject.com/Articles/42036/Project-Metadata-Generation-using-T4
As Anvaka points out, using a base class for your view model that checks property names can help avoid this particular problem (though it won't tell you when your VM class does its own property-change notification and ignores the method in the base class, not that I've ever seen anything like that happen in my code).
And you can (and should) instrument your code so that things that aren't working fail in a way that's visible to you. The thing that's kind of paradoxical about this is that if you know what things may fail and you watch them, they won't, because the fact that you're watching them will keep you from making the mistakes that lead them to fail (like writing a template selector that doesn't always return a template).
But fundamentally, the view is the UI, so I would be pretty surprised to find methods of testing it that weren't also methods for testing the UI.
Using WPF has made me a fan of INotifyPropertyChanged. I like to use a helper that takes an expression and returns the name as a string (see example code below). In lots of applications I see by very proficient programmers, however, I see code that handles the strings raw (see 2nd example below). By proficient I mean MVP types who know how to use Expressions.
To me, the opportunity for having the compiler catch mistakes in addition to easy refactoring makes the Exression approach better. Is there an argument in favor of using raw strings that I am missing?
Cheers,
Berryl
Expression helper example:
public static string GetPropertyName<T>(Expression<Func<T, object>> propertyExpression)
{
Check.RequireNotNull<object>(propertyExpression, "propertyExpression");
switch (propertyExpression.Body.NodeType)
{
case ExpressionType.MemberAccess:
return (propertyExpression.Body as MemberExpression).Member.Name;
case ExpressionType.Convert:
return ((propertyExpression.Body as UnaryExpression).Operand as MemberExpression).Member.Name;
}
var msg = string.Format("Expression NodeType: '{0}' does not refer to a property and is therefore not supported",
propertyExpression.Body.NodeType);
Check.Require(false, msg);
throw new InvalidOperationException(msg);
}
Raw strings example code (in some ViewModelBase type class):
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG"), DebuggerStepThrough]
public void VerifyPropertyName(string propertyName) {
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null) {
string msg = "Invalid property name: " + propertyName;
if (ThrowOnInvalidPropertyName) throw new Exception(msg);
else Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
To me, the opportunity for having the compiler catch mistakes in addition to easy refactoring makes the Exression approach better. Is there an argument in favor of using raw strings that I am missing?
I agree, and personally, use the expression approach in my own code, in most cases.
However, there are two reasons I know of to avoid this:
It is less obvious, especially to less experienced .NET developers. Writing RaisePropertyChanged(() => this.MyProperty ); is not always as obvious to people as RaisePropertyChanged("MyProperty");, and doesn't match framework samples, etc.
There is some performance overhead to using expressions. Personally, I don't feel this is really that meaningful of a reason, since this is usually used in data binding scenarios (which are already slow due to reflection usage), but it is potentially a valid concern.
The benefit of using the TypeDescriptor approach is that it enables dynamic property scenarios based on ICustomTypeDescriptor implementations where the implementation can effectively create dynamic property metadata on the fly for a type that is being described. Consider a DataSet whose "properties" are determined by the result set it is populated with for example.
This is something that expressions does not provide however because it's based on actual type information (a good thing) as opposed to strings.
I wound up spending more time on this than I expected, but did find a solution that has a nice mix of safety/refactorability and performance. Good background reading and an alternate solution using reflection is here. I like Sacha Barber's solution even better (background here.
The idea is to use an expression helper for a property that will participate in change notification, but only take the hit once for it, by storing the resulting PropertyChangedEventArgs in your view model. For example:
private static PropertyChangedEventArgs mobilePhoneNumberChangeArgs =
ObservableHelper.CreateArgs<CustomerModel>(x => x.MobilePhoneNumber);
HTH,
Berryl
Stack walk is slow and lambda expression is even slower. We have solution similar to well known lambda expression but almost as fast as string literal. See
http://zamboch.blogspot.com/2011/03/raising-property-changed-fast-and-safe.html
A CallerMemberName attribute was introduced in .net 4.5
This attribute can only be attached to optional string parameters and if the parameter is not used by caller in the function call then name of the caller will be passed in the string parameter
This removes the need to specify name of property when raising the PropertyChanged event thus it works with refactoring and because the changes are done at compile time there's no difference in performance.
Below is an example of implementation and more info can be found at http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspx and http://msdn.microsoft.com/en-us/library/hh534540.aspx
public class DemoCustomer : INotifyPropertyChanged
{
string _customerName
public string CustomerName
{
get { return _customerNameValue;}
set
{
if (value != _customerNameValue)
{
_customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I am trying to unit test my WPF databindings using the test suit provided by Microsoft Team System. I would like to be able to test the bindings without showing the window because most of my tests will be for user controls and not actually on a window. Is this possible or is there a better way to do it? The code below works if I show the window, but if I don't, the bindings don't update.
Window1_Accessor target = new Window1_Accessor();
UnitTestingWPF.Window1_Accessor.Person p = new UnitTestingWPF.Window1_Accessor.Person() { FirstName = "Shane" };
Window1 window = (target.Target as Window1);
window.DataContext = p;
//window.Show(); //Only Works when I actually show the window
//Is it possible to manually update the binding here, maybe? Is there a better way?
Assert.AreEqual("Shane", target.textBoxFirstName.Text); //Fails if I don't Show() the window because the bindings aren't updated
While looking for a solution to convert WPF binding errors into exception, I figured out that it can also be used in a unit test project.
The technique is very simple:
Derive a TraceListener that throws instead of logging
Add that listener to PresentationTraceSources.DataBindingSource
Please see the complete solution on GitHub, it includes a unit test project.
Shane, if what you're really worried about is a binding breaking silently, you should look at redirecting the binding traces to somewhere you can examine. I'd start here:
http://blogs.msdn.com/mikehillberg/archive/2006/09/14/WpfTraceSources.aspx
Other than that, I agree with Gishu that bindings aren't good candidates for unit testing, mainly due to the automagic going on that Gishu mentioned in the "Epilogue". Instead focus on making sure the underlying class behaves correctly.
Note, too, that you can get even more robust traces using the PresentationTraceSources class:
http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.aspx
Hope that helps!
Eyeball it.
This kind of declarative markup rarely breaks.. unless someone goes in manual and screws it up. Even then, you can fix it within minutes. IMHO the cost of writing such tests far outweigh the benefits.
Update[Dec3,08]: Alrighty then.
The test is just testing that the textbox has the value "FirstName" as the Path property of the binding. If I change/refactor FirstName to JustName in the actual data source object, the test would still pass since it is testing against an anonymous type. (Green test when code broken - TDD Antipattern: The Liar)
If your aim is to verify that FirstName has been specified in XAML,
Assert.AreEqual("FirstName", txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).ParentBinding.Path.Path);
If you really must catch broken bindings via unit tests (and don't want to show the UI), use the real data source... struggled for a while and came up with this.
[Test]
public void TestTextBoxBinding()
{
MyWindow w = new MyWindow();
TextBox txtBoxToProbe = w.TextBox1;
Object obDataSource = w; // use 'real' data source
BindingExpression bindingExpr = BindingOperations.GetBindingExpression(txtBoxToProbe, TextBox.TextProperty);
Binding newBind = new Binding(bindingExpr.ParentBinding.Path.Path);
newBind.Source = obDataSource;
txtBoxToProbe.SetBinding(TextBox.TextProperty, newBind);
Assert.AreEqual("Go ahead. Change my value.", txtBoxToProbe.Text);
}
Epilogue:
There's some real covert stuff happening in the call to Window.Show(). It somehow magically sets up the DataItem property after which data binding starts working.
// before show
bindingExpr.DataItem => null
bindingExpr.Status => BindingStatus.Unattached
// after show
bindingExpr.DataItem => {Actual Data Source}
bindingExpr.Status => BindingStatus.Active
Once the Binding is Active, I guess you can force textbox updates via code like this..
txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
Once again, I voice my reluctance against this approach. Getting NUnit to run in STA was a pain..
Combining advice I came across in a number of SO posts I wrote the following class which works very well to test WPF bindings.
public static class WpfBindingTester
{
/// <summary>load a view in a hidden window and monitor it for binding errors</summary>
/// <param name="view">a data-bound view to load and monitor for binding errors</param>
public static void AssertBindings(object view)
{
using (InternalTraceListener listener = new InternalTraceListener())
{
ManualResetEventSlim mre = new ManualResetEventSlim(false);
Window window = new Window
{
Width = 0,
Height = 0,
WindowStyle = WindowStyle.None,
ShowInTaskbar = false,
ShowActivated = false,
Content = view
};
window.Loaded += (_, __) => mre.Set();
window.Show();
mre.Wait();
window.Close();
Assert.That(listener.ErrorMessages, Is.Empty, listener.ErrorMessages);
}
}
/// <summary>Is the test running in an interactive session. Use with Assume.That(WpfBindingTester.IsAvailable) to make sure tests only run where they're able to</summary>
public static bool IsAvailable { get { return Environment.UserInteractive && Process.GetCurrentProcess().SessionId != 0; } }
private class InternalTraceListener : TraceListener
{
private readonly StringBuilder _errors = new StringBuilder();
private readonly SourceLevels _originalLevel;
public string ErrorMessages { get { return _errors.ToString(); } }
static InternalTraceListener() { PresentationTraceSources.Refresh(); }
public InternalTraceListener()
{
_originalLevel = PresentationTraceSources.DataBindingSource.Switch.Level;
PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Error;
PresentationTraceSources.DataBindingSource.Listeners.Add(this);
}
public override void Write(string message) {}
public override void WriteLine(string message) { _errors.AppendLine(message); }
protected override void Dispose(bool disposing)
{
PresentationTraceSources.DataBindingSource.Listeners.Remove(this);
PresentationTraceSources.DataBindingSource.Switch.Level = _originalLevel;
base.Dispose(disposing);
}
}
}
you can try Guia.
With it you can unit-test your UserControl and check if the data binding is correct. You have to show the window though.
Here is an example. It starts a new instance of your UserControl and sets its DataContext and then checks if the textbox is set to the right value.
[TestMethod]
public void SimpleTest()
{
var viewModel = new SimpleControlViewModel() {TextBoxText = "Some Text"};
customControl = CustomControl.Start<SimpleUserControl>((control) => control.DataContext = viewModel);
Assert.AreEqual("Some Text", customControl.Get<TextBox>("textbox1").Value);
customControl.Stop();
}