« TDD 最初の一歩 (C#編) #tddadventjp | トップページ | TDD Advent Calendar 2013 »

2013年12月13日 (金)

MSTest‐Windows ストア アプリ開発の暗黒大陸 #win8dev_jp #tddadventjp #tddnet

このエントリーは、 TDD Advent Calendar 2013 の 13日目です。
このエントリーは、 Windows Store App Advent Calendar 2013 の 13日目です。
13日の金曜日です。
 

暗黒大陸 - それは内部がほとんど知られず、地図も無い世界
20131213_tddadvent01
※ この画像は広辞苑

Windows ストア アプリを開発するのに使う Visual Studio 2012 / 2013 には、 テスト ハーネス フレームワークとして MSTest が標準装備されています。 これは無償の Express エディションにも搭載されているので、 Windows ストア アプリを開発するときに TDD しない訳にはいきません。 (← TDD の話はこれだけw)

てことで、 テスト コードを書き始めましょうか。
MSTest とは長い付き合いですからね、 サクサク書けますよ。

でもまぁ、 たまにはヘルプを見ましょうか。
20131213_tddadvent02

…へ!?

20131213_tddadvent03

「このトピックはこのライブラリには含まれていません」

ぉひぉひ… (@_@;)

使っている Assert クラスは、 Microsoft.VisualStudio.TestPlatform.UnitTestFramework 名前空間のもの。
20131213_tddadvent04

この名前空間をぐぐってみる

……ほぇ!?

MSDN に載ってない!! (・_・)

つまり、

20131213_tddadvent05

Windows ストア アプリ開発で使ってる MSTest は、 地図の無い暗黒大陸だった!

# いや~、 VS2012 が出てから丸1年以上経ってるんですからね、 まさかまだドキュメントが公開されてないとは思わなかったよw

 

■ 探検の旅へ

となればしかたがありません。 なぜ人は探検の旅へ出るのか、 そこに未踏の地があるからだ (と、誰かがそう言ったかどうかは知らないw)。

◇ オブジェクト ブラウザーで一覧してみる

ほっ、 よかった。 Visual Studio のオブジェクト ブラウザーで、 さすがに一覧は出てきます。

20131213_tddadvent06

左側は従来のアプリ用、 右側は Windows ストア アプリ用。
Windows ストア アプリ用の方が、 かなり少ないです。

従来のアプリ用の一覧で、 Windows ストア アプリ用には無いモノを赤くすると、 ↓こんな感じ。
20131213_tddadvent07

上の一覧で、 黒いのは同じモノがWindows ストア アプリ用にもあります。
で、 従来のアプリ用の MSTest ライブラリーは、 しっかり MSDN にドキュメントがあります。 てことで、 黒いところは、 従来のアプリ用のドキュメントを見れば、 たぶんだいたい同じでしょう。 f(^^;

赤いところは、 Windows ストア アプリ用には無いモノ。 無いものは無いのです、 気にしてもしかたありませんw

で。
問題は、 Windows ストア アプリ用にしか存在しないモノ。 次の Windows ストア アプリ用の一覧で緑色にしたモノたち。

20131213_tddadvent08

この 8箇所は、 まさに秘境!

  • DataRowAttribute
  • DataTestMethodAttribute
  • FrameworkMessages
  • Logger
  • Logger.LogMessageHandler
  • TestResult
  • AppContainer.Assert
  • AppContainer.UITestMethodAttribute

 

◇ AppContainer.Assert クラス

秘境探検の第一歩は、 AreEqual メソッドなどを持ってる Assert クラスとは別に、 違う名前空間 AppContainer を定義してそこに置かれている Assert クラスから行ってみましょう。

どんなメソッドを持っているかは、 オブジェクト ブラウザーで分かります。

この Assert クラスは ThrowsException メソッドだけを持っているようです。

しかし、オブジェクト ブラウザーで見ても、 使い方がワカリマセン orz
20131213_tddadvent09

となると、 メソッドのシグネチャだけを頼りに、 手探りで使い方を調べるしかありません。
いろいろ探検した結果、 こういう使い方をするようです。

【Assert.ThrowsException メソッドの使い方】

1. まず、1つ目の Assert クラスと名前が衝突しないよう、using でクラス名のエイリアスを切っておく。

using AppContainerAssert = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer.Assert;

2. ThrowsException<T>メソッドのシグネチャ

public static Task<ExpectedException> //返値: 発生した例外
ThrowsException<ExpectedException>( //型: 想定される例外
  Func<Task> action,  //引数1: テスト対象の非同期メソッド
  string message,     //引数2: 不合格時に出すメッセージ
  params object[] parameters //引数3: ……不明 orz
)where ExpectedException : Exception;


3. ThrowsException<T>メソッドの使用例

[TestMethod]
public async Task Test_想定通りの例外が出れば合格() {
  var resultEx
    = await AppContainerAssert.ThrowsException <InvalidOperationException>(
        () => TestTargetMethod("INVALIDOP"),
        "想定した例外が出なかったよ!!"
      );

  // 出てきた例外のチェックも可能
  Assert.AreEqual<string>("例外のメッセージだよ", resultEx.Message);
}


まぁ、NUnit の Assert.Throws と同じですね。

# っていうか、 メソッド名から同じなんじゃないかと推測できたので、 使い方が分かったようなもんですが f(^^;

…と、 ここまでは 11月のわんくま同盟名古屋勉強会で喋ったところ ⇒ わんくま名古屋 #29 (2013/11/23) TDD道場 #17
20131213_tddadvent12

 

◇ AppContainer.UITestMethod 属性

AppContainer 名前空間にもうひとつあるのが、UITestMethod 属性。

しかしこいつは、 オブジェクト ブラウザーで見ても引数を取らないので、 もう名前だけが頼り。
う~ん…

20131213_tddadvent10

あ、 TestMethod 属性と同じ Execute メソッドも持ってるから、 同じように使うのかな?
ってか、 TestMethod 属性のアタマに 「UI」 って付いてるんだから、 UI のユニット テストが出来るんかな?

その方向で試してみましょう。

TDD するんじゃなくて、 クラス ライブラリーの使い方を調べたいだけなので、 テスト対象となるコードから先に用意します。

VS2013 で Windows ストア アプリのプロジェクトを作り、 UI にアクセスするプロパティとメソッドをメイン画面のコードビハインドに書きます。

(MainPage.xaml.cs ファイル)

public sealed partial class MainPage : Page
{
  public string PageTitle
  {
    get { return this.pageTitle.Text; }
    set { this.pageTitle.Text = value; }
  }

  public void ChangeTitle(string newTitle)
  {
    this.pageTitle.Text = newTitle;
  }

  //以下略

これらプロパティとメソッドは、 どれも UI の TextBlock コントロールにアクセスしているので、 UI スレッド以外のスレッドから呼び出すと例外が出ます。

続いて、 Windows ストア アプリ用のテスト プロジェクトを作り、 上のプロジェクトに参照設定をしたら、 こんなテストコードを普通に書いてみます。

using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using SampleApp;

namespace MSTestTest
{
  [TestClass]
  public class UnitTest1
  {
    [TestMethod]
    public void TestMethod1()
    {
      var mainPage = new MainPage();
      Assert.AreEqual<string>("My Application", mainPage.PageTitle);

      mainPage.ChangeTitle("New Title");
      Assert.AreEqual<string>("New Title", mainPage.PageTitle);
    }
    //以下略


これは、 UI スレッドとは違うスレッドから UI に触ることになるので、 例外が出て失敗するはずです。
確認のため、 テストを流してみます。

はい、 失敗。
こんな失敗メッセージが出ました。

テスト成果:    失敗

結果  のメッセージ:    テスト メソッド MSTestTest.UnitTest1.TestMethod1 が例外をスローしました:
System.Exception: アプリケーションは、別のスレッドにマーシャリングされたインターフェイスを呼び出しました。 (HRESULT からの例外:0x8001010E (RPC_E_WRONG_THREAD))。テスト内で UI オブジェクトを使用している場合は、[TestMethod] の代わりに [UITestMethod] 属性を使用して UI スレッド内でテストを実行することを検討してください。

ぅん、 予定通りだ、 問題無い。

……あ! orz

メッセージをよく見ると、 その中にこんなことが!
⇒ 「[TestMethod] の代わりに [UITestMethod] 属性を使用して UI スレッド内でテストを実行することを検討してください」

ぐふぅ… いろいろ四苦八苦したわたしの苦労を返せ~っ (吐血

【UITestMethod 属性の使い方】

1. using で名前空間のエイリアスを切っておく。(単に using すると Assert クラスが衝突する)

using AC = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer;

2. [TestMethod] の代わりに [UITestMethod] 属性を使う

//[TestMethod]
[AC.UITestMethod]
public void TestMethod1()
{
  var mainPage = new MainPage();
  Assert.AreEqual<string>("My Application", mainPage.PageTitle);

  mainPage.ChangeTitle("New Title");
  Assert.AreEqual<string>("New Title", mainPage.PageTitle);
}


はい、 ↓できました♪

20131213_tddadvent11

 

今回はここまで。

しかし、 まだあと 6箇所も残ってます。

俺たちの旅はまだ始まったばかりだ!!

|

« TDD 最初の一歩 (C#編) #tddadventjp | トップページ | TDD Advent Calendar 2013 »

*コラム」カテゴリの記事

<MSTest>」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/209349/58744972

この記事へのトラックバック一覧です: MSTest‐Windows ストア アプリ開発の暗黒大陸 #win8dev_jp #tddadventjp #tddnet:

« TDD 最初の一歩 (C#編) #tddadventjp | トップページ | TDD Advent Calendar 2013 »