我正在尝试使用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
答案 0 :(得分:6)
在寻找将WPF绑定错误转换为异常的解决方案时,我发现它也可以在单元测试项目中使用。
技术非常简单:
TraceListener
而不是记录PresentationTraceSources.DataBindingSource
请参阅complete solution on GitHub,其中包括单元测试项目。
答案 1 :(得分: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
希望有所帮助!
答案 2 :(得分:1)
眼球。
这种声明性标记很少会破坏..除非有人进入手册并将其搞砸。即使这样,你也可以在几分钟内解决它。恕我直言,编写此类测试的成本远大于其中的好处。
更新 [Dec3,08]:好的,然后 测试只是测试文本框的值为“FirstName”作为绑定的Path属性。如果我在实际数据源对象中将FirstName更改/重构为JustName,则测试仍会通过,因为它是针对匿名类型进行测试的。 (代码破坏时的绿色测试 - TDD Antipattern:The Liar) 如果您的目的是验证是否已在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
一旦Binding处于活动状态,我想你可以通过这样的代码强制文本框更新。
txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
再一次,我表达了我对这种做法的不情愿。让NUnit在STA中运行是一件痛苦的事。
答案 3 :(得分: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 :(得分: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();
}