2008-12-01 8 views
10

Ich versuche, meine WPF-Databinds mit der von Microsoft Team System bereitgestellten Testversion zu testen. Ich möchte in der Lage sein, die Bindungen zu testen, ohne das Fenster zu zeigen, da die meisten meiner Tests für Benutzersteuerelemente und nicht tatsächlich in einem Fenster sind. Ist das möglich oder gibt es einen besseren Weg? Der folgende Code funktioniert, wenn ich das Fenster zeige, aber wenn nicht, werden die Bindungen nicht aktualisiert.Komponententest WPF-Bindungen

  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 

Antwort

2

Shane, wenn, worüber du dir wirklich Sorgen machst, ist eine Bindung, die still bricht, du solltest die verbindenden Spuren dorthin umleiten, wo du sie untersuchen kannst. Ich würde hier beginnen:

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

Other than that, ich mit Gishu einig, dass Bindungen sind keine guten Kandidaten für Unit-Tests, vor allem wegen der Automagic auf, die in der „Epilogue“ erwähnt Gishu gehen. Konzentrieren Sie sich stattdessen darauf, sicherzustellen, dass sich die zugrunde liegende Klasse korrekt verhält.

Beachten Sie auch, dass Sie noch robuste Spuren mit der PresentationTraceSources Klasse erhalten:

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

Hoffnung, das hilft!

1

Augapfel es.
Diese Art von deklarativen Markup bricht selten .. es sei denn, jemand geht in das Handbuch und verschraubt es. Selbst dann können Sie es innerhalb von Minuten beheben. IMHO die Kosten für das Schreiben solcher Tests bei weitem überwiegen die Vorteile.

Aktualisieren [Dec3,08]: Alrighty dann.
Der Test testet gerade, dass das Textfeld den Wert "FirstName" als Path-Eigenschaft der Bindung hat. Wenn ich FirstName im tatsächlichen Datenquellenobjekt in JustName ändere/umgestalte, würde der Test noch bestehen, da er mit einem anonymen Typ getestet wird. (Grün-Test, wenn der Code gebrochen - TDD Antipattern: Der Lügner) Wenn Ihr Ziel zu überprüfen ist, dass First in XAML festgelegt wurde,

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

Wenn Sie wirklich gebrochen Bindungen über Unit-Tests fangen müssen (und dies nicht tun will die UI zeigen), nutze die echte Datenquelle ... kämpfte eine Weile und kam dabei auf.

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

Epilog: Es gibt einige real covert stuff-Window.Show() im Aufruf geschieht. Es stellt irgendwie magisch die Eigenschaft DataItem ein, nach der Datenbindung anfängt zu arbeiten.

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

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

Sobald die Bindung aktiv ist, ich denke, Sie Textbox-Updates über Code wie diese zwingen kann ..

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

Noch einmal, ich meine Abneigung gegen diesen Ansatz äußern. NUnit in STA laufen zu lassen, war ein Schmerz.

+2

Wenn wir Eigenschaften in einer Klasse sind verbindlich, und die Klasse Refactoring wird die XAML noch kompilieren, aber keine Ausnahme wird geworfen und unsere App wird nicht mehr richtig funktionieren, da die Bindungen nicht korrekt sein werden. Das war schon ein Thema für uns, deshalb suchen wir nach einer Lösung. – NotDan

0

können Sie versuchen Guia. Mit diesem können Sie Ihr UserControl Unit-Test testen und prüfen, ob die Datenbindung korrekt ist. Sie müssen das Fenster jedoch zeigen.

Hier ist ein Beispiel.Es startet eine neue Instanz von Ihrem UserControl und legt seinen DataContext fest und überprüft dann, ob das Textfeld auf den richtigen Wert festgelegt ist.

1

Kombinieren Beratung Ich kam in einer Reihe von SO-Posts Ich schrieb die folgende Klasse, die sehr gut funktioniert, um WPF-Bindungen zu testen.

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

Während nach einer Lösung suchen WPF Fehler in Ausnahme konvertieren Bindung, habe ich herausgefunden, dass es auch in einem Unit-Test-Projekt verwendet werden können.

Die Technik ist sehr einfach:

  1. Leiten Sie eine TraceListener, die anstelle von Logging wirft
  2. hinzufügen, dass die Zuhörer PresentationTraceSources.DataBindingSource

Bitte beachten Sie die complete solution on GitHub enthält es eine Unit-Test-Projekt.

Failed test in Visual Studio