2

これは、そこにある糸脱毛中毒者のためのものです。私はこの方法を持っています:

    public void RefreshMelts()
    {
        MeltsAvailable.Clear();

        ThreadPool.QueueUserWorkItem(delegate
        {
            Dispatcher.BeginInvoke((ThreadStart)delegate
            {
                eventAggregator.GetEvent<BusyEvent>().Publish(true);
                eventAggregator.GetEvent<StatusMessageEvent>().Publish(
                    new StatusMessage("Loading melts...", MessageSeverity.Low));
            });

            try
            {
                IList<MeltDto> meltDtos = meltingAppService.GetActiveMelts();

                Dispatcher.Invoke((ThreadStart)delegate
                {
                    foreach (MeltDto availableMelt in meltDtos)
                    {
                        MeltsAvailable.Add(availableMelt);
                    }
                    OnPropertyChanged("MeltsAvailable");

                    eventAggregator.GetEvent<BusyEvent>().Publish(false);
                    eventAggregator.GetEvent<StatusMessageEvent>().Publish(
                        new StatusMessage("Melts loaded", MessageSeverity.Low));
                });
            }
            catch (ApplicationException ex)
            {
                log.Error("An error occurred in MeltsViewModel when attempting to load melts", ex);

                Dispatcher.Invoke((ThreadStart)delegate
                {
                    MeltsAvailable.Clear();

                    eventAggregator.GetEvent<StatusMessageEvent>().Publish(
                        new StatusMessage("Melt data could not be loaded because an error occurred; " +
                            "see the application log for detail",
                            MessageSeverity.High));
                    eventAggregator.GetEvent<BusyEvent>().Publish(false);
                });
            }

        });

    }

これは、WPFユーザーコントロールで定義されています。MeltsAvailableは、MeltDtosのObservableCollectionです。このコードは、アプリケーション自体で実行すると美しく機能します。

問題は、このメソッドの結果を検証するために、NMockを使用して単体テストを作成したいということです。具体的には、一度呼び出されると、MeltsAvailableプロパティにいくつかの項目があります。テスト方法は次のとおりです。

    [TestMethod]
    public void GetAvailableMeltsTest()
    {
        MeltDto mockMelt1 = new MeltDto();
        MeltDto mockMelt2 = new MeltDto();

        mockMelt1.MeltIdentifier = "TST0001";
        mockMelt2.MeltIdentifier = "TST0002";

        IList<MeltDto> availableMelts = new List<MeltDto>();
        availableMelts.Add(mockMelt1);
        availableMelts.Add(mockMelt2);

        Expect.Exactly(1).On(service).Method("GetActiveMelts").Will(Return.Value(availableMelts));


        MeltsViewModel vm = new MeltsViewModel(aggregator, logger, service, configManagerFactory); // All of these are mock objects

        vm.RefreshMelts();
        Thread.Sleep(millisecondDelayForEventPublish * 100);

        mockery.VerifyAllExpectationsHaveBeenMet();

        Assert.AreEqual(vm.MeltsAvailable.Count, 2);
        Assert.AreEqual(vm.MeltsAvailable[0].MeltIdentifier, "TST0001");
        Assert.AreEqual(vm.MeltsAvailable[1].MeltIdentifier, "TST0002");

    }

テストは、最初のAssert.AreEqualで一貫して失敗します。その時点でvm.MeltsAvailableは空です。

私がすべての糸を取り除き、それをそのままにしておくと:

    public void RefreshMelts()
    {
        MeltsAvailable.Clear();
        IList<MeltDto> meltDtos = meltingAppService.GetActiveMelts();
        foreach (MeltDto availableMelt in meltDtos)
        {
            MeltsAvailable.Add(availableMelt);
        }
        OnPropertyChanged("MeltsAvailable");
    }

テストに合格します。

したがって、明らかに、スレッドには気に入らない点がありますが、[デバッグ]->[例外]->[CLR例外]->[スロー]をオンにし、[マイコードのみ]をオフにしても、RefreshMeltsでは例外はまったく発生しません。

最も奇妙な部分は、MeltDtoオブジェクトをMeltsAvailableコレクションにロードするDispatcher.Invoke呼び出しが呼び出されていないように見えることです。セクション全体をブレークポイントで覆うことができますが、ブレークポイントがヒットすることはありません。私のテストでThread.Sleep時間を10秒まで上げても、何も変わりません。

なんで?そのセクションが実行されないのはなぜですか、ステップインまたはブレークできないのはなぜですか、例外が発生しないのはなぜですか、実行では正常に機能するのにテストでは機能しないのはなぜですか?

どうもありがとう、スティーブ

4

1 に答える 1

5

Dispatcherは、実行中のスレッドに関連付けられているメッセージループです。メインスレッドがアイドル状態のときに、キュー内のアイテムを処理します。ユニットテストでは、それは決して起こりません。スレッドはビジーであり、テストが完了すると終了します。

Visual Studioを使用してテストを実行している場合は、コードカバレッジの強調表示をオンにすると、Dispatcher.Invoke()内のコードが呼び出されないことがわかります(赤で表示されます)。

DispatcherFrameを使用して、Dispatcherをトリガーしてキューに入れられたメッセージを処理できます。次のヘルパークラスを単体テストプロジェクトに追加します。

public static class DispatcherHelper 
{ 
    public static void DoEvents() 
    {
        DispatcherFrame frame = new DispatcherFrame(); 
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
        Dispatcher.PushFrame(frame); 
    } 

    private static object ExitFrame(object frame) 
    { 
        ((DispatcherFrame)frame).Continue = false; 
        return null; 
    } 
}

テストの最後(アサーションの前)に、DispatcherHelper.DoEvents()を呼び出します。これにより、Dispatcherがトリガーされ、ビューモデルの監視可能なコレクションにアイテムを追加するイベントなどの未解決のイベントが処理されます。次に、ビューモデルのプロパティを調べて、正しく設定されていることを確認できます。

于 2011-03-11T07:01:42.807 に答える