11

我正在尝试使用 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
4

5 回答 5

7

在寻找将 WPF 绑定错误转换为异常的解决方案时,我发现它也可以用于单元测试项目。

该技术非常简单:

  1. 派生一个TraceListener抛出而不是记录的
  2. 将该侦听器添加到PresentationTraceSources.DataBindingSource

在 GitHub 上查看完整的解决方案,它包括一个单元测试项目。

Visual Studio 中的测试失败

于 2013-10-26T18:45:10.113 回答
2

目测吧。
这种声明性标记很少会中断..除非有人手动输入并将其搞砸。即使这样,您也可以在几分钟内修复它。恕我直言,编写此类测试的成本远远超过收益。

更新[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(). 它以某种方式神奇地设置了 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 中运行很痛苦。

于 2008-12-01T17:58:31.577 回答
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

希望有帮助!

于 2008-12-03T15:17:45.940 回答
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);
        }
    }
}
于 2012-07-13T09:16:50.560 回答
0

你可以试试东望洋。有了它,您可以对您的 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();
    }
于 2010-11-04T19:13:35.560 回答