簡體   English   中英

為什么RelayCommand RaiseCanExecuteChanged不能在單元測試中工作?

[英]Why doesn't RelayCommand RaiseCanExecuteChanged work in a unit test?

我正在使用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的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.

    ...
}

此行在Dispatcher工作項隊列中放置帶有DispatcherPriority Background的工作項。 該工作項應該通知CommandManager.RequerySuggested事件的所有處理程序。

問題是這個工作項永遠不會運行。

解決方案是強制調度程序運行工作項。

我在MVVM Foundation 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);

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM