gpt4 book ai didi

c# - 为什么 RelayCommand RaiseCanExecuteChanged 在单元测试中不起作用?

转载 作者:可可西里 更新时间:2023-11-01 09:09:05 26 4
gpt4 key购买 nike

我正在使用 Nuget (4.1.23.0) 上可用的当前版本的 MvvmLight,调用 RaiseCanExecuteChanged 似乎在单元测试中没有做任何事情。场景很简单,我有一个命令:

public RelayCommand FooCommand { get; private set; }

我在 View 模型构造函数中新建它并将它指向一些私有(private)方法:

FooCommand = new RelayCommand(Foo, CanFoo);

private void Foo()
{
// do some fooing.
}

private bool CanFoo()
{
return SomeRequiredProperty != null;
}

然后在 SomeRequiredProperty 的 setter 中我调用 RaiseCanExecuteChanged:

public object SomeRequiredProperty
{
get
{
return someRequiredProperty;
}

set
{
someRequiredProperty = value;
FooCommand.RaiseCanExecuteChanged();
}
}

现在在单元测试中,我执行以下操作:

// Arrange
var canExecuteChanged = false;
viewModel.FooCommand.CanExecuteChanged += (sender, args) => canExecuteChanged = true;

// Act
viewModel.SomeRequiredProperty = new object();

// Assert
Assert.That(canExecuteChanged, Is.True);

测试失败,因为我的事件处理程序没有触发。这是为什么?

更新:该行为确实在运行时有效。

最佳答案

已修复!

nemesv是正确的,因为 FooCommand.RaiseCanExecuteChanged() 只是调用 CommandManager.InvalidateRequerySuggested()

除此之外,FooCommand.CanExecuteChanged 只是将处理程序转发给 CommandManager.RequerySuggested 事件:

public event EventHandler CanExecuteChanged
{
add
{
...
CommandManager.RequerySuggested += value;
}
...
}

问题的原因CommandManager 中的以下代码行类:

private void RaiseRequerySuggested()
{
...
_requerySuggestedOperation = dispatcher.
BeginInvoke(
DispatcherPriority.Background,
new DispatcherOperationCallback(RaiseRequerySuggested),
null); // dispatcher is the Dispatcher for the current thread.

...
}

此行放置一个 DispatcherPriority 的工作项背景 关于 Dispatcher工作项队列。工作项应该通知 CommandManager.RequerySuggested 事件的所有处理程序。

问题 是此工作项从未运行过。

解决方案是强制调度员运行工作项。

我在 this discussion 中找到了解决方案在 MVVM 基金会 CodePlex 页面上。我设法将代码简化为以下帮助程序类。

public static class DispatcherTestHelper
{
private static DispatcherOperationCallback exitFrameCallback = ExitFrame;

/// <summary>
/// Synchronously processes all work items in the current dispatcher queue.
/// </summary>
/// <param name="minimumPriority">
/// The minimum priority.
/// All work items of equal or higher priority will be processed.
/// </param>
public static void ProcessWorkItems(DispatcherPriority minimumPriority)
{
var frame = new DispatcherFrame();

// Queue a work item.
Dispatcher.CurrentDispatcher.BeginInvoke(
minimumPriority, exitFrameCallback, frame);

// Force the work item to run.
// All queued work items of equal or higher priority will be run first.
Dispatcher.PushFrame(frame);
}

private static object ExitFrame(object state)
{
var frame = (DispatcherFrame)state;

// Stops processing of work items, causing PushFrame to return.
frame.Continue = false;
return null;
}
}

我的测试现在看起来像这样:

// Arrange
var canExecuteChanged = false;
viewModel.FooCommand.CanExecuteChanged +=
(sender, args) => canExecuteChanged = true;

// Act
viewModel.SomeRequiredProperty = new object();
DispatcherTestHelper.ProcessWorkItems(DispatcherPriority.Background);

// Assert
Assert.That(canExecuteChanged, Is.True);

而且,最重要的是,它通过了 :)

关于c# - 为什么 RelayCommand RaiseCanExecuteChanged 在单元测试中不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12033798/

26 4 0