Unit test Привязки WPF
Я пытаюсь использовать unit test мои данные в WPF, используя тестовый пример, предоставленный Microsoft Team System. Я хотел бы иметь возможность проверить привязки, не показывая окно, потому что большинство моих тестов будут для пользовательских элементов управления, а не на самом деле в окне. Возможно ли это или есть лучший способ сделать это? Код ниже работает, если я покажу это окно, но если этого не произойдет, привязки не будут обновляться.
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
Шейн, если то, о чем вы действительно беспокоитесь, - это обязательное нарушение молчания, вы должны посмотреть на перенаправление следов привязки туда, где вы можете изучить. Я бы начал здесь:
http://blogs.msdn.com/mikehillberg/archive/2006/09/14/WpfTraceSources.aspx
Кроме этого, я согласен с Гишу в том, что привязки не являются хорошими кандидатами для модульного тестирования, в основном из-за того, что Гига, упомянутая в "Эпилоге", была автоматически опубликована. Вместо этого сосредоточьтесь на том, чтобы убедиться, что базовый класс ведет себя правильно.
Заметьте, что вы можете получить еще более надежные трассы, используя класс PresentationTraceSources:
http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.aspx
Надеюсь, что это поможет!
Ответ 2
При поиске решения для преобразования ошибок привязки WPF в исключение, я понял, что он также может использоваться в проекте unit test.
Техника очень проста:
- Вывести
TraceListener
, который выдает вместо записи
- Добавьте этот прослушиватель в
PresentationTraceSources.DataBindingSource
Пожалуйста, см. полное решение на GitHub, оно включает проект unit test.
![Failed test in Visual Studio]()
Ответ 3
Eyeball it.
Такая декларативная разметка редко ломается. Если кто-то не входит в руководство и не закручивает его. Даже тогда вы можете исправить это в течение нескольких минут. ИМХО, стоимость написания таких тестов намного превышает преимущества.
Обновить [Dec3,08]: Тогда, хорошо.
Тест просто проверяет, что текстовое поле имеет значение "FirstName" как свойство Path привязки. Если я изменяю/рефакторизую FirstName на JustName в фактическом объекте источника данных, тест все равно пройдет, поскольку он тестирует анонимный тип. (Зеленый тест при разрыве кода - TDD Antipattern: The Liar)
Если ваша цель - проверить, что FirstName указано в XAML,
Assert.AreEqual("FirstName", txtBoxToProbe.GetBindingExpression(TextBox.TextProperty).ParentBinding.Path.Path);
Если вы действительно должны ломать сломанные привязки с помощью модульных тестов (и не хотите показывать пользовательский интерфейс), используйте реальный источник данных... борется какое-то время и придумали это.
[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 было болью.
Ответ 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();
}