10

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

次に、セッターでSomeRequiredPropertyRaiseCanExecuteChangedを呼び出します。

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

イベントハンドラーが起動していないため、テストは失敗します。何故ですか?

更新:この動作は実行時に実際に機能します。

4

1 に答える 1

9

修理済み!

nemesvFooCommand.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作業項目 キューに配置します。作業項目は、すべてのハンドラーにイベントを通知することになっています。BackgroundCommandManager.RequerySuggested

問題は、この作業項目が実行されないことです。

解決策は、ディスパッチャに作業項目の実行を強制することです。

MVVMFoundationCodePlexページのこのディスカッションで解決策を見つけました。私はなんとかコードを次のヘルパークラスにいくらか単純化することができました。

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

そして、最も重要なことに、それは合格します:)

于 2012-08-24T02:05:34.033 に答える