单元测试 WPF 绑定
Posted
技术标签:
【中文标题】单元测试 WPF 绑定【英文标题】:Unit test WPF Bindings 【发布时间】:2010-09-24 18:40:04 【问题描述】:我正在尝试使用 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
【问题讨论】:
【参考方案1】:在寻找将 WPF 绑定错误转换为异常的解决方案时,我发现它也可以用于单元测试项目。
技术很简单:
-
派生一个
TraceListener
,它会抛出而不是记录
将该监听器添加到PresentationTraceSources.DataBindingSource
请查看complete solution on GitHub,它包含一个单元测试项目。
【讨论】:
【参考方案2】:Shane,如果您真正担心的是绑定无声地中断,您应该考虑将绑定跟踪重定向到您可以检查的地方。我会从这里开始:
http://blogs.msdn.com/mikehillberg/archive/2006/09/14/WpfTraceSources.aspx
除此之外,我同意 Gishu 的观点,即绑定不是单元测试的好选择,主要是因为 Gishu 在“尾声”中提到的自动魔法。而是专注于确保底层类的行为正确。
还要注意,您可以使用 PresentationTraceSources 类获得更可靠的跟踪:
http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.aspx
希望有帮助!
【讨论】:
【参考方案3】:关注一下。 这种声明性标记很少中断..除非有人手动输入并将其搞砸。即使这样,您也可以在几分钟内修复它。恕我直言,编写此类测试的成本远远超过收益。
更新[2008 年 12 月 3 日]:好的。 该测试只是测试文本框的值“FirstName”作为绑定的 Path 属性。如果我在实际数据源对象中将 FirstName 更改/重构为 JustName,则测试仍然会通过,因为它正在针对匿名类型进行测试。 (代码损坏时的绿色测试 - TDD 反模式:骗子) 如果您的目标是验证是否已在 XAML 中指定 FirstName,
Assert.AreEqual("FirstName", txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).ParentBinding.Path.Path);
如果您真的必须通过单元测试来捕获损坏的绑定(并且不想显示 UI),请使用真实的数据源...挣扎了一段时间并想出了这个。
[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);
结语:
在对Window.Show()
的调用中发生了一些real covert stuff。它以某种方式神奇地设置了 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 中运行很痛苦..
【讨论】:
如果我们绑定到类中的属性并重构该类,xaml 仍将编译但不会抛出异常,并且我们的应用程序将不再正常运行,因为绑定将不正确。这对我们来说已经是一个问题,这就是我们正在寻找解决方案的原因。【参考方案4】:结合我在许多 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);
【讨论】:
【参考方案5】:你可以试试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();
【讨论】:
以上是关于单元测试 WPF 绑定的主要内容,如果未能解决你的问题,请参考以下文章
android 数据绑定单元测试错误无法解析数据绑定编译器选项。参数: