単体テストコードでコントロールのイベントを発生させる

Kinkuma FrameworkのInvokeCommandActionの機能拡張をしてて困ったことが1つあった。
それは、「コントロールのイベントを使うような単体テストコードってどうやって書けば良いの!?」ってこと。

System.Windows.Interactivity.InvokeCommandAction クラスは、通常EventTriggerの子要素とそして定義されるから、このアクションが呼ばれたときに正しい動作をするかどうかなんてイベントを発生させるしかない(と思う)。

で、コントロールのイベントって外部からどうやって発生させるんだっけか?
って調べたらちゃんとSystem.Windows.UIElement.RaiseEvent メソッドというのがあった。

このメソッドを使えば外部からでもイベントを発生させられるので、例えば

        /// <summary>
        /// Buttonクリックイベントで起動するInvokeCommandActionをテストする
        /// </summary>
        [TestMethod]
        public void TestMethod1()
        {
            // 1.検証用の変数を用意する
            var actual = false; 

            // 2.テスト対象のTriggerActionを作成する
            var targetAction = new InvokeCommandAction();

            // 3.TriggerActionにCommandを設定する。Commandが呼ばれた場合はactualがtrueになる
            targetAction.Command = new DelegateCommand(() => actual = true);

            // 4.イベントを発生させるコントロールを作成する。
            var eventSource = new Button();

            // 5.EventTriggerを作成する。
            var trigger = new System.Windows.Interactivity.EventTrigger("Click");

            // 6.EventTriggerにテスト対象のTriggerActionを追加する。
            trigger.Actions.Add(targetAction);

            // 7.EventTriggerをボタンコントロールにアタッチさせ、SourceObjectを設定する。
            trigger.Attach(eventSource);
            trigger.SourceObject = eventSource;

            // 8.ボタンのクリックイベントを発生させる
            eventSource.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));

            // trueならば、Commandは呼ばれている
            Assert.IsTrue(actual);
        }

みたいな書き方ができる。
この例はあくまで流れを書いただけなので余り意味が無いが、
応用することでCommandParameterを検証したり、別のTriggerActionをテストしたりできるようになるだろう。

ちなみに、他のイベント(例えばListBoxのSelectionChangedイベントとか)を発生させたい場合は
上記の4と5と8を変更すれば良い。

これでEventTriggerActionのテストも怖くないぜ(たぶん)