kkamegawa's weblog

Visual Studio,TFS,ALM,VSTS,DevOps関係のことについていろいろと書いていきます。Google Analyticsで解析を行っています

Visual Studio 2012 Update2 CTPでのUIスレッドテスト機能

Visual Studio 2012 Update2に搭載される予定の新機能です。今までmstestではこんなテストを書くことはできませんでした。

    [TestMethod]
    public void MyTest()
    {
        TextBlock temp = new TextBlock();
        temp.Text = "Hello World";
    }

実行するとこんな例外が起きます。

テスト名: MyTest
テストの完全名: UnitTestLibrary1.UnitTest1.MyTest
テスト ソース: c:\Workspace\Private\StoreApps\UnitTestProject1\UnitTestLibrary1\UnitTest1.cs : 行 71
テスト成果: 失敗
テスト継続時間: 0:00:00.0081156

結果 のメッセージ: テスト メソッド UnitTestLibrary1.UnitTest1.MyTest が例外をスローしました:
System.Exception: アプリケーションは、別のスレッドにマーシャリングされたインターフェイスを呼び出しました。 (HRESULT からの例外: 0x8001010E (RPC_E_WRONG_THREAD)). If you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread.
結果 のスタック トレース:
at Windows.UI.Xaml.Controls.TextBlock..ctor()
at UnitTestLibrary1.UnitTest1.MyTest() in c:\Workspace\Private\StoreApps\UnitTestProject1\UnitTestLibrary1\UnitTest1.cs:line 72

これはUIコンポーネントはUIを動かすための単一スレッドで動かなければならないためです。ながーくなるので、岩永さんのわかりやすい解説を参照してください。他にもいっぱいこの手の解説があります。
非同期処理とディスパッチャー | ++C++; // 未確認飛行 C ブログ
少なくともMSTestはマルチスレッドで動いていて、UIスレッドではないスレッドから動かされるために例外が発生します。もちろん頑張ればMSTestでも書くことはできました。

    public TestContext TestContext { get; set; }
    private TextBlock testTextBlock;

    public async void ExecuteOnUI(Action fn)
    {
        Exception failure = null;

        await Windows.ApplicationModel.Core.CoreApplication.
        MainView.CoreWindow.Dispatcher.RunAsync(
            Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                try
                {
                    fn();
                }
                catch (Exception ex)
                {
                    failure = ex;
                }
            });
        if (failure != null) {
            Logger.LogMessage(failure.StackTrace);
            throw failure;
        }
    }

    [TestInitialize]
    public void TestInitialize()
    {
        ExecuteOnUI(() =>
        {
            testTextBlock = new TextBlock();
        });
    }

    [TestMethod]
    public void TestMethod1()
    {
        ExecuteOnUI(() =>
        {
            testTextBlock.Text = "Hello World";
        });
    }

でも、そんな苦労もUpdate2でさようならです。Windows ストアアプリケーションの単体テストに限り(惜しいですね…)、こんな風に書くことができます。

using Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer;
//(ship)
    [UITestMethod]
    public void MyTest()
    {
        TextBlock temp = new TextBlock();
        temp.Text = "Hello World";
    }

Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainerにUITestMethod関係の機能が追加されたようです。これでUIスレッドで動かなければならないもののテストが簡単にできますね!