Unidad de prueba WPF vinculaciones
unit-testing data-binding (5)
Globo ocular.
Este tipo de marcado declarativo rara vez se rompe ... a menos que alguien entre en manual y lo estropee. Incluso entonces, puedes arreglarlo en minutos. En mi humilde opinión, el costo de escribir tales pruebas supera con creces los beneficios.
Actualización [Dic3,08]: Bien entonces.
La prueba solo está probando que el cuadro de texto tiene el valor "Nombre" como la propiedad Ruta del enlace. Si cambio / refactorizo FirstName a JustName en el objeto de fuente de datos real, la prueba aún pasaría, ya que está probando contra un tipo anónimo. (Prueba verde cuando se rompe el código - TDD Antipattern: The Liar) Si su objetivo es verificar que FirstName se haya especificado en XAML,
Assert.AreEqual("FirstName", txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).ParentBinding.Path.Path);
Si realmente debe atrapar enlaces rotos mediante pruebas unitarias (y no quiere mostrar la interfaz de usuario), use la fuente de datos real ... luchó durante un tiempo y se le ocurrió esto.
[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);
}
Epílogo: Hay algunas cosas encubiertas reales sucediendo en la llamada a Window.Show()
. De alguna manera mágicamente configura la propiedad DataItem después de la cual el enlace de datos comienza a funcionar.
// before show
bindingExpr.DataItem => null
bindingExpr.Status => BindingStatus.Unattached
// after show
bindingExpr.DataItem => {Actual Data Source}
bindingExpr.Status => BindingStatus.Active
Una vez que la vinculación está activa, supongo que puede forzar las actualizaciones de cuadros de texto a través de un código como este ..
txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
Una vez más, expreso mi reticencia a este enfoque. Obtener NUnit para ejecutar en STA fue un dolor ...
Estoy tratando de probar de forma unitaria mis conexiones de datos de WPF usando la prueba provista por Microsoft Team System. Me gustaría poder probar los enlaces sin mostrar la ventana porque la mayoría de mis pruebas serán para controles de usuario y no en una ventana. ¿Es esto posible o hay una mejor manera de hacerlo? El siguiente código funciona si muestro la ventana, pero si no lo hago, los enlaces no se actualizan.
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
Shane, si lo que realmente te preocupa es una ruptura de enlace en silencio, deberías ver cómo redirigir las trazas vinculantes hacia un lugar que puedas examinar. Yo comenzaría aquí:
http://blogs.msdn.com/mikehillberg/archive/2006/09/14/WpfTraceSources.aspx
Aparte de eso, estoy de acuerdo con Gishu en que los enlaces no son buenos candidatos para pruebas unitarias, principalmente debido a la marcha automática que Gishu mencionó en el "Epílogo". En su lugar, concéntrese en asegurarse de que la clase subyacente se comporte correctamente.
Tenga en cuenta también que puede obtener trazas aún más robustas utilizando la clase PresentationTraceSources:
http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.aspx
¡Espero que ayude!
puedes probar Guia . Con él puede probar su UserControl y verificar si el enlace de datos es correcto. Tienes que mostrar la ventana sin embargo.
Aquí hay un ejemplo. Inicia una nueva instancia de UserControl y establece su DataContext y luego verifica si el cuadro de texto está configurado con el valor correcto.
[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();
}
Combinando consejos que encontré en varias publicaciones SO escribí la siguiente clase que funciona muy bien para probar enlaces 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);
}
}
}
Mientras buscaba una solución para convertir los errores de encuadernación WPF en una excepción, descubrí que también se puede usar en un proyecto de prueba unitario.
La técnica es muy simple:
- Derive un
TraceListener
que lanza en lugar de iniciar sesión - Agregue ese oyente a
PresentationTraceSources.DataBindingSource
Por favor, vea la solución completa en GitHub , incluye un proyecto de prueba de unidad.