Welcome to Habanero Land!   Register  |  Login
You are here:   Blogs
  So, why do you need to register to download Habanero and access the additional materials? 
The success of any open source project is based on the community built around it. So go ahead, register / login and get involved!
Dec
19
Sun
Posted By Andrew on Sunday, December 19, 2010
128 Views


This is the fifth blog in a series of blogs I have put together whilst trying to find a solution for unit testing WPF Bindings.

Now that I have a method that can match bindings to properties, it’s time to re-factor the code into a Binding Tester class. This is what I came up with.

    public class BindingTest  
    {
        public FrameworkElement Root { get; private set; }
 
        public void OnFrameWorkElement(FrameworkElement root)
        {
            Root = root;
        }
 
        public BindingTestResult ForProperty(string boundPropertyName)
        {
            return TestBindingForProperty(boundPropertyName, Root);
        }
 
        private static BindingTestResult TestBindingForProperty(string boundPropertyName, FrameworkElement root)
        {
            foreach (FrameworkElement element in LogicalTreeHelper.GetChildren(root).OfType())
            {
                FieldInfo[] properties =
                    element.GetType().GetFields(BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static |
                                                BindingFlags.FlattenHierarchy);
                foreach (FieldInfo field in properties)
                {
                    if (field.FieldType == typeof(DependencyProperty))
                    {
                        DependencyProperty dp = (DependencyProperty)field.GetValue(null);
                        if (BindingOperations.IsDataBound(element, dp))
                        {
                            BindingExpression bindingExpression = BindingOperations.GetBindingExpression(element, dp);
                            if (typeof(TBindingObjectType) == bindingExpression.DataItem.GetType())
                            {
                                const bool ignoreCase = true;
                                if (string.Compare(boundPropertyName, bindingExpression.ParentBinding.Path.Path)==0)
                                {
                                    return new BindingTestResult(BindingMatchTypes.ExactMatch, "Binding exists");
                                }
                                if (string.Compare(boundPropertyName, bindingExpression.ParentBinding.Path.Path, 
						ignoreCase) == 0)
                                {
                                    // Test ignoring case3
                                    return new BindingTestResult(BindingMatchTypes.FailedMatchOnCase, 
			string.Format("Binding '{0}' does not match Property '{1}'. Please check the case.", 
			bindingExpression.ParentBinding.Path.Path, boundPropertyName));
                                }
                            }
                        }
                    }
                }
 
                // Not found so check all child elements
                var bindingTestResult = TestBindingForProperty(boundPropertyName, element);
                if (bindingTestResult.BindingMatchType == BindingMatchTypes.ExactMatch || 
			bindingTestResult.BindingMatchType == BindingMatchTypes.FailedMatchOnCase)
                {
                    return bindingTestResult;
                }
            }
            return new BindingTestResult(BindingMatchTypes.FailedMatch, 
			String.Format("Binding for Property '{0}' does not exist", boundPropertyName));
        }
    }

    public enum BindingMatchTypes
    {
        ExactMatch = 0,
        FailedMatchOnCase = 1,
        FailedMatch = 2

    }

    public class BindingTestResult
    {
        public BindingMatchTypes BindingMatchType { get; private set; }
        public string Message { get; private set; }

        public BindingTestResult(BindingMatchTypes bindingMatchType, string message)
        {
            BindingMatchType = bindingMatchType;
            Message = message;
        }
    }

To write a test with this code would look like:

        [RequiresSTA]
        [Test]
        public void Test_WindowWithCorrectBindings_Databindings2ndTry()
        {
            var part = new ComputerPart
                       {
                           Description = "Seagate 500Gb HDD",
                           PartCode = "HDD001",
                           Price = 1250,
                           Stock = 10,
                           Cost = 950
                       };
            part.Save();
 
            var partWindow = new WindowWithCorrectBindings {DataContext = part};

            partWindow.Show();
            try
            {
                var bindingTest = new BindingTest();

                bindingTest.OnFrameWorkElement(partWindow);

                var testBindingForPropertyDescription = bindingTest.ForProperty("Description");
                Assert.IsTrue(testBindingForPropertyDescription.BindingMatchType==BindingMatchTypes.ExactMatch, 
			testBindingForPropertyDescription.Message);
 
                var testBindingForPropertyPrice = bindingTest.ForProperty("Price");
                Assert.IsTrue(testBindingForPropertyPrice.BindingMatchType==BindingMatchTypes.ExactMatch, 
				testBindingForPropertyPrice.Message);
 
                var testBindingForPropertyStock = bindingTest.ForProperty("Stock");
                Assert.IsTrue(testBindingForPropertyStock.BindingMatchType==BindingMatchTypes.ExactMatch, 
			testBindingForPropertyStock.Message);
 
                var testBindingForPropertyCost = bindingTest.ForProperty("Cost");
                Assert.IsTrue(testBindingForPropertyCost.BindingMatchType==BindingMatchTypes.ExactMatch, 
			testBindingForPropertyCost.Message);
 
            }
            finally
            {
                partWindow.Close();
            }           

Some of the code in the test may look a little strange. I am using Habanero for my business object layer (Habanero is an open source ORM tool). The first few lines of the code in the unit test are setting up an instance object of the type that will be bound to WindowWithCorrectBindings DataContext. The BindingTest class itself is set up once we have created the Window, bound the DataContext and then call window.Show(). BindingTest accepts a generic type which it the type of the bound object. Next we need to let the BindingTest know about the window which contains the bindings. We do this with a call to bindingTest.OnFrameworkElement(partWindow). After that, each call to bindingWindow.ForProperty will return a BindingResult that has a BindingMatchType and a Message property. A test that passes will return a BindingMatchType of BindingMatchTypes.Exact.

Note, once we have called window.Show we need to wrap the remainder of our code in a try..finally block and call window.Close in the finally block to ensure that we clean up the window after the test. Two other important steps when writing test using the BindingTest class are:

  • Test methods need to be decorated with the [RequiresSTA] attribute
  • A TearDown method needs to be added to the test fixture with the following line of code.

System.Windows.Threading.Dispatcher.CurrentDispatcher.InvokeShutdown();
Categories

 Top Viewed
 Habanero Cloud
 Archive
 

This website is best viewed in Internet Explorer 7 & 8; Firefox 3.6.11; Opera 10.63; & Safari 4.