What is development without unit testing?
For me, it is just chaos! You make a bug fix and you end up giving birth to another 3… Also if you don't have unit tests, refactoring becomes a risk and you end up saying "IF IT WORKS, DON'T TOUCH IT"...
When developing software using WPF, you will hit a brick wall when creating your first unit test. You will encounter the
InvalidOperationException curse. The NUnit test runner will show you the red light and tell you:
InvalidOperationException failed to set the specified COM apartment state. If you try to run a unit test with the
TestDriven.Net Visual Studio plugin, this will not happen since this tool runs in STA yet 99% of your build server does not... So we have to work around this issue!
The Cause of this Problem
The cause of this problem is that NUnit runs in an MTA while WPF must run in STA. I found the solution to this problem here. I used this class (with some minor changes) in my
AvalonUnitTesting library. Basically, to run unit tests that are WPF related, you can use a class called
AvalonTestRunner (in the AvalonUnitTesting.dll supplied with this article) and call the
RunInSta method passing a
delegate. This will auto magically switch the current thread apartment to STA for you… Strictly speaking, you will not switch the apartment state of the thread, but instead start a new thread in STA and run the
delegate inside that thread. An example of such a test with the
AvalonTestRunner would look like this:
public void TestMainWindow()
MainWindow window = new MainWindow();
More Than Just an Assert
WPF is a very cool, flexible and powerful platform... I just felt like saying it
Anyway, back to unit testing... With WPF you can also test user interaction like, for example, a button click. I found a very interesting article on this here.
Basically, you can use
AutomationPeers to raise events that are normally raised when a user does something with your app. I did not add any support for this in my library, but maybe someday (if my wife lets me) I will...
Unit Testing DataBinding
When you say "WPF", the first thing that comes to mind is the DataBinding feature. I guess DataBinding is one of the strongest features in WPF. Yet when you have an error "OOPS" nothing happens and nothing works. The reason why this happens is that WPF catches the exception for us and logs the error in the Trace (silent, but deadly). In actual fact, when there is a binding error, you can see the error in the Visual Studio debug window which is the default trace listener.
It would be great if there was a way in which you can unit test databinding, wouldn't it? So I decided to dig into this and have a look at what one can do. I found a very good article on WPF tracing over here. This article shed some light on what is happening and how I can create a small class that can unit test databinding for me. I said, why don't I create a trace listener that can assert whenever a WPF databinding exception is raised. Basically this solution can make your XAML databinding testable! So I created a class that listens to these databinding warnings and asserts to show me that there is a problem in the XAML. Basically the class (
AvalonTraceListener) I implemented is just a
TraceListener that asserts when WPF logs an error. So in the
WriteLine method you override your
public override void WriteLine(string message)
if (currentWindowBeingTested != null)
So the code to test a Window or a control for data binding would look something like this:
public void TestDataBindingForControls()
The process of testing the Window/control is very simple. You pass an instance of the object that contains your XAML and the
RunDataBindingTests method of the
AvalonTestRunner will check if the control is a Window, if not it will wrap the control you passed in a Window control. When we have a Window instance, then we call the
Show method of the Window. This will cause all databinding to be initialized and if there are any errors, our
TraceListener will make sure to catch them and fail the unit test.
It is very important that your control is not using resources from the
Application.Resources or any other shared resource since that would cause the test to fail. For example, if you have a
ResourceDictionary that is merged with the
Application.Resources, the test will fail since that
Resource is not found. What I would suggest to do in this scenario is to overload the constructor of your Window/control and make sure you add the resource you need just for unit testing, i.e.:
public Window1() : this(false)
public Window1(bool unitTesting)
Then pass the instance for the unit test passing
true in the constructor ->
It is very important that you merge the
Resource you need before calling
InitializeComponents since this will try to load your XAML. Once you have all this in place, there you have it. You can now start unit testing WPF DataBinding as well....
Download the library and have loads of WPF fun.
- 22nd December, 2007: Initial post