2008-12-01 120 views
10

我想單元測試我的WPF數據綁定,使用Microsoft Team System提供的測試套裝。我希望能夠在不顯示窗口的情況下測試綁定,因爲我的大多數測試都是針對用戶控件,而不是實際上在窗口上。這是可能的還是有更好的方法來做到這一點?下面的代碼工作,如果我顯示窗口,但如果我不這樣做,綁定不會更新。單元測試WPF綁定

  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 

回答

2

Shane,如果你真的擔心的是一個無聲的破壞綁定,你應該看看重定向綁定痕跡到你可以檢查的地方。我會從這裏開始:

http://blogs.msdn.com/mikehillberg/archive/2006/09/14/WpfTraceSources.aspx

其他,我有Gishu同意綁定,則不適合進行單元測試,主要是由於AUTOMAGIC正在進行中的「後記」中提到Gishu。而是專注於確保基礎類的行爲正確。

注意,那就是,你可以得到更強大的痕跡使用PresentationTraceSources類:

http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.aspx

希望幫助!

1

眼球吧。
這種聲明性標記很少打破..除非有人進入手動並將其擰緊。即使這樣,你可以在幾分鐘之內解決它。恕我直言,編寫這些測試的成本遠遠超過了好處。

更新 [Dec3,08]:那好吧。
測試僅測試文本框的值爲「FirstName」作爲綁定的Path屬性。如果我在實際的數據源對象中將FirstName更改/重構爲JustName,那麼測試仍然會通過,因爲它正在針對匿名類型進行測試。 (綠測試時,代碼破 - TDD反模式:說謊者) 如果你的目的是驗證名字已經在XAML被指定,

Assert.AreEqual("FirstName", txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).ParentBinding.Path.Path); 

如果你真的必須趕通過單元測試打破綁定(以及不想要展示用戶界面),使用真實的數據源...掙扎了一段時間,並想出了這一點。

[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); 
} 

結語: 有一些real covert stuff發生在調用Window.Show()。它奇蹟般地設置DataItem屬性,之後數據綁定開始工作。

// before show 
bindingExpr.DataItem => null 
bindingExpr.Status => BindingStatus.Unattached 

// after show 
bindingExpr.DataItem => {Actual Data Source} 
bindingExpr.Status => BindingStatus.Active 

一旦綁定活動,我想你可以通過代碼迫使這樣的文本框更新..

txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).UpdateTarget(); 

,我再次表達我對這種做法不情願。讓NUnit在STA上運行是件痛苦的事情。

+2

如果我們在類結合的特性,並重構類,XAML中仍然會編譯,但沒有將引發異常,我們的應用程序將無法再正常的綁定將是不正確的。這對我們來說是一個問題,這就是我們尋找解決方案的原因。 – NotDan 2008-12-02 15:48:23

0

你可以試試Guia。 有了它,您可以對您的UserControl進行單元測試,並檢查數據綁定是否正確。你必須顯示窗口。

這裏是一個例子。它啓動一個新的UserControl實例並設置其DataContext,然後檢查文本框是否設置爲正確的值。

[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(); 
    } 
1

結合的意見,我在多個崗位SO我寫了下面的類,它工作得非常好測試WPF綁定碰到。

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); 
     } 
    } 
} 
4

在尋找將WPF綁定錯誤轉換爲異常的解決方案時,我發現它也可以在單元測試項目中使用。

該技術是非常簡單的:

  1. 派生一個TraceListener拋出,而不是記錄
  2. 該偵聽器添加到PresentationTraceSources.DataBindingSource

請參閱complete solution on GitHub,它包括一個單元測試項目。

Failed test in Visual Studio