SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

C#で始めるテスト駆動開発入門

ユニバーサルWindowsアプリのユニットテスト(後編)

C#で始めるテスト駆動開発入門(9)

ダウンロード CsTdd09.zip (129.9 KB)

6-2. Assert.ThrowsException

 例外が出ることを確かめるテストは、従来のデスクトップアプリ用のMSTestではExpectedException属性を使って次のように書いていました。

テストコード: 従来の、例外が出ることを確かめるテストの例(ストアアプリでは使えない)
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void TestSay0Error() {
  string result = this._testTarget.Say(0);
  Assert.Fail("例外が発生せず、'{0}'が返りました。", result);
}

 Windowsストアアプリでは、Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer名前空間のAssertクラスにThrowsExceptionメソッドがあり、それを使います。AreEqualメソッドなどを持っているAssertクラスは、別にMicrosoft.VisualStudio.TestPlatform.UnitTestFramework名前空間にあるので、クラス名の衝突を避けるために1手間余計に掛かります。

 ThrowsExceptionメソッドは、型引数に期待される例外の型を与え、第1引数には実行するテストのラムダ式(ただし、Task型を返さねばならない)、第2引数には失敗時のメッセージ文字列を与えます。また、ThrowsExceptionメソッドは、成功した時には発生した例外オブジェクトを返してきますので、その後で例外の内容を検査することもできます(次のコード)。

 Windows Phoneでは、Assert.ThrowsExceptionはサポートされておらず、また、従来のExpectedException属性も使えないため、trycatchするしかありません。WindowsとPhoneで共有したいテストでは、この書き方をすることになります(次のコード)。

テストコード: FizzBuzzでn = 0のときは例外を出す
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;

#if WINDOWS_APP
// Assertクラスの名前衝突を避けるため、クラス名のエイリアスを切っておく
using AppContainerAssert
  = Microsoft.VisualStudio.TestPlatform.UnitTestFramework
      .AppContainer.Assert;
#endif

namespace CsTdd08Tests.Shared
{
  [TestClass]
  public class FizzBuzzPlayerTest
  {

    // n=0のときは、例外が出る

#if WINDOWS_APP
    // WindowsではThrowsExceptionが使えるので、簡潔に書ける
    [TestMethod]
    public async Task TestSay0Error()
    {
      CsTdd08App.FizzBuzzPlayer p = new CsTdd08App.FizzBuzzPlayer();

      // ThrowsException で、期待する型の例外が出ることをチェック
      var resultEx
        = await AppContainerAssert
            .ThrowsException<ArgumentOutOfRangeException>(
              () => Task.Run(() => p.Say(0)),
              "想定した例外が出ませんでした。"
            );
      // 例外のメッセージをチェック
      StringAssert.StartsWith(
              resultEx.Message,
              "FizzBuzz ゲームは 1 から始まります。"
            );
    }
#endif

#if WINDOWS_PHONE_APP
    // PhoneではThrowsExceptionが使えないので、try~catchするしかない
    [TestMethod]
    public void TestSay0Error()
    {
      CsTdd08App.FizzBuzzPlayer p = new CsTdd08App.FizzBuzzPlayer();

      string result = null;
      try
      {
        result = p.Say(0);
      }
      catch (ArgumentOutOfRangeException ex)
      {
        // 想定した例外が出た。
        // 例外のメッセージをチェックする。
        StringAssert.StartsWith(
                ex.Message,
                "FizzBuzz ゲームは 1 から始まります。"
              );
        return;
      }
      catch (Exception ex)
      {
        Assert.Fail("想定外の例外'{0}'が返されました。", ex.GetType().Name);
      }

      Assert.Fail("例外が発生せず、'{0}'が返されました。", result);
    }
#endif

  }
}

6-3. UITestMethod

 UI部品をテストするときは、UIスレッドで実行する必要があります。そのためには、Dispatcherを使って次のようなテストコードを書いていました。

テストコード: Dispatcherを使ってUIスレッドでテストを実行する(これはWindows/Phoneともに動作する)
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;
using Windows.UI.Xaml.Controls;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;

namespace CsTdd08Tests.Shared
{
  [TestClass]
  public class MainPageTest
  {
    [TestMethod]
    public async Task SetTitleTextTest()
    {
      var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
      await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
      {
        // UIスレッドでテストを実行する

        var frame = new Frame();
        frame.Navigate(typeof(CsTdd08App.MainPage));

        // フレームのコンテンツはMainPageのはず
        var mainPage = frame.Content as CsTdd08App.MainPage;
        Assert.IsNotNull(mainPage);

        // MainPageにSetTitleTextメソッドとTitleTextプロパティを実装する
        mainPage.SetTitleText("新しいタイトル");
        Assert.AreEqual<string>("新しいタイトル", mainPage.TitleText);
      });
    }
  }
}

 Windowsストアアプリでは、UITestMethod属性を使って簡潔に書けるようになりました(次のコード)。Dispatcherを取得する方法で悩まなくて済むのもありがたいです。

テストコード: UITestMethod属性を使ってUIスレッドでテストを実行する(Phoneでは動作しない)
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;
using Windows.UI.Xaml.Controls;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;

#if WINDOWS_APP
// 名前空間のエイリアスを切っておく。(単にusingするとAssertクラスが衝突する)
using AC = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.AppContainer;
#endif

namespace CsTdd08Tests.Shared
{
  [TestClass]
  public class MainPageTest
  {
#if WINDOWS_APP
    // WindowsではUITestMethod属性が使える
    [AC.UITestMethod]
    public void SetTitleTextTest_Windows()
    {
      var frame = new Frame();
      frame.Navigate(typeof(CsTdd08App.MainPage));

      // フレームのコンテンツはMainPageのはず
      var mainPage = frame.Content as CsTdd08App.MainPage;
      Assert.IsNotNull(mainPage);

      // MainPageにSetTitleTextメソッドとTitleTextプロパティを実装する
      mainPage.SetTitleText("新しいタイトル");
      Assert.AreEqual<string>("新しいタイトル", mainPage.TitleText);
    }
#endif
  }
}

まとめ

  • Shared Project Reference Managerを使って共有プロジェクトを作り、そこにユニットテストを置くこともできる(現在は有償版のVisual Studioのみ)
  • 非同期メソッドをテストするには、テストメソッドのシグネチャに「async Task」を付ける
  • パラメタライズドテストが可能になった(DataTestMethod属性/DataRow属性)
  • 例外が出ることを確かめるテストはAssert.ThrowsExceptionで(Windowsストアアプリのみ)
  • テストをUIスレッドで実行するにはUITestMethod属性(Windowsストアアプリのみ)

 今回は前後編に渡って、ユニバーサルプロジェクトでユニットテストを作る方法と、MSTestの新機能について解説してきました。いかがだったでしょうか、ユニットテストも「ユニバーサル」に書ける自信を持ってもらえたと思います。

サンプルソースのUI

 パラメタライズドテストのところでFizzBuzzPlayerクラスを作りました。記事で解説する余裕はなかったのですが、サンプルソースにはそのUIも実装してあります。
 

  • CsTdd08App.Shared/FizzBuzzPlayer.cs:FizzBuzzのロジック(記事中で作成)
  • CsTdd08App.Shared/UI/FizzBuzzViewModel.cs:UIにバインドするクラス(TDDで実装)
  • CsTdd08App.Shared/UI/FizzBuzzControl.xaml:FizzBuzzのUI(ユーザーコントロール)
  • CsTdd08Tests.Shared/UI/FizzBuzzViewModelTest.cs:FizzBuzzViewModelのユニットテスト

 FizzBuzzViewModelはごく簡単なものですが、それでもFizzBuzzViewModelTestには26個のテストメソッドがあります(実行される回数はその2倍の52回)。UIにバインドするためのプロパティやコマンドをTDDで実装するときの参考にしてください。

 

サンプルソースにはFizzBuzzのUIも実装されている
サンプルソースにはFizzBuzzのUIも実装されている
サンプルソースのFizzBuzzViewModelTest
サンプルソースのFizzBuzzViewModelTest

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
C#で始めるテスト駆動開発入門連載記事一覧

もっと読む

この記事の著者

biac(ばいあっく)

HONDA R&Dで自動車の設計をやっていた機械屋さんが、技術の進化スピードに魅かれてプログラマーに。以来30年ほど、より良いコードをどうやったら作れるか、模索の人生。わんくま同盟の勉強会(名古屋)で、よく喋ってたりする。2014/10~2019/6 Microsoft MVP (Windows Devel...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/8093 2014/10/08 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー