Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / WPF
Article

WPF Unit Testing

Rate me:
Please Sign up or sign in to vote.
4.71/5 (14 votes)
22 Dec 2007CPOL4 min read 89.5K   1.6K   49   2
How to unit test WPF applications

Introduction

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:

C#
[Test]
public void TestMainWindow()
{
    AvalonTestRunner.RunInSTA(
    delegate
    {
        MainWindow window = new MainWindow();
        Assert.AreEqual(300, window.Height);
    });
}

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 Assert.

C#
public override void WriteLine(string message)
{
    if (currentWindowBeingTested != null)
    currentWindowBeingTested.Close();
    Assert.Fail(message);
}

So the code to test a Window or a control for data binding would look something like this:

C#
[Test]
public void TestDataBindingForControls()
{
    AvalonTestRunner.RunInSTA(delegate
    {
        AvalonTestRunner.RunDataBindingTests(new MainWindow());

        AvalonTestRunner.RunDataBindingTests(new UserControlTests());
    });
}

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.:

C#
public Window1() : this(false)
{}

public Window1(bool unitTesting)
{
    if(unitTesting)
        Resources.MergedDictionaries.Add(new BrushesResources());
    InitializeComponent();
} 

Then pass the instance for the unit test passing true in the constructor -> new Window1(true).

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.

History

  • 22nd December, 2007: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Other Uniblue Systems (www.uniblue.com)
Malta Malta
Check out my Blog
http://marlongrech.wordpress.com/

Currently building a WPF Controls library aka AvalonControlsLibrary
http://marlongrech.wordpress.com/avalon-controls-library/

Comments and Discussions

 
Questionnice Pin
BillW332-May-12 8:37
professionalBillW332-May-12 8:37 
GeneralThanks! Pin
User 27100931-Dec-07 16:12
User 27100931-Dec-07 16:12 
Marlon,

Thank you for sharing with us. This will prod me to get into NUnit.

Best to you,

Cheers, Karl

Just a grain of sand on the worlds beaches.


modified 27-Feb-21 21:01pm.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.