【问题标题】:Why doesn't RelayCommand RaiseCanExecuteChanged work in a unit test?为什么 RelayCommand RaiseCanExecuteChanged 在单元测试中不起作用?
【发布时间】:2012-08-15 13:19:38
【问题描述】:

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

public RelayCommand FooCommand { get; private set; }

我在视图模型构造函数中新建它并指向一些私有方法:

FooCommand = new RelayCommand(Foo, CanFoo);

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

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

然后在 SomeRequiredProperty 的设置器中调用 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);

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

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

【问题讨论】:

  • RaiseCanExecuteChanged 方法只调用CommandManager.InvalidateRequerySuggested(),因此它不会直接引发事件。我想这会导致您的测试失败,但我不知道如何解决它。在运行时命令正在运行,对吗?
  • 对,这是有道理的。谢谢你。方法名称确实暗示否则嘿?我还没有将 UI 连接到这个视图模型的阶段,但是在选择框架之前我使用 MvvmLight 做了一个峰值,并且 CanExecute 功能运行良好。
  • 快速连接起来以测试和更新问题。

标签: c# wpf mvvm mvvm-light relaycommand


【解决方案1】:

已修复!

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 Background 的工作项放在Dispatcher 工作项队列中。工作项应该通知CommandManager.RequerySuggested 事件的所有处理程序。

问题是这个工作项永远不会运行。

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

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

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);

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-19
    • 2019-11-26
    • 1970-01-01
    相关资源
    最近更新 更多