はじめに
前回はSilverlightで単体テストを行うための環境の構築と、
今回、
テストクラスの準備
初めにSilverlightプロジェクトのPageクラスをテストするためのテストクラスを作成しましょう。このテストクラスは後ほど紹介するUIのテストと非同期のテストの両方で使用します。
[新しい項目の追加]でテンプレートに[Silverlight Test Class]を選択し、
追加されたPageTest.
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Silverlight.Testing;
namespace SLUnitTest.Tests
{
  [TestClass]
  public class PageTest : SilverlightTest  //①
  {
    private Page page = null;
  
    [TestInitialize]  //②
    public void Pageの初期化()
    {
      page = new Page();
      //③
      this.Silverlight.TestSurface.Children.Add(page);
    }
  }
}
まず①ではSilverlightTestクラスを親クラスに設定しています。このSilverlightTestクラスは以下のような機能を持ちます。
- RootVisualとHTML DOMとのブリッジ機能
 - Sleepなどのヘルパーメソッド
 - 非同期テストのためのヘルパーメソッド
 
続いて②ではTestInitialize属性が付加された
「Pageの初期化メソッド」
では、
テストの対象となる機能を追加
Pageクラスに以下のような機能を追加し、
- ボタンが押されたら、
TextBlockに 「Hello world」 と表示される。  
まずはPageクラスに上記の機能を追加します。Page.
<StackPanel>
  <TextBlock x:Name="messageTextBlock" />
  <Button x:Name= "messageButton" 
    Click="Button_Click" Content="メッセージ" />
</StackPanel>
次にボタンがクリックされた時の処理をPage.
private void Button_Click(object sender, RoutedEventArgs e)
{
  messageTextBlock.Text = "Hello world";
}
UIのテストコードを追加
さっそくテストコードを記述したいところですが、
今回取り上げているテスティングフレームワークはキーボードやマウス操作のシミュレートを行うことはできません。そのため、
具体的には対象となるクラス、
/// <summary>
/// for test
/// </summary>
internal void SimulateButtonClick()
{
  Button_Click(messageButton,new RoutedEventArgs());
}
ここではSimulateButtonClickというメソッドを定義し、
このSimulateButtonClickメソッドはinternalで定義されています。このメソッドを別のアセンブリであるテストプロジェクトのアセンブリから呼び出せるように、
[assembly: InternalsVisibleTo("SLUnitTest.Tests")]
上記のInternalVisivleTo属性を設定することで、
では、
[TestMethod]
public void ボタンクリックでラベルにHelloworldが表示されるべき()
{
  //①
  Assert.IsNull(page.messageTextBlock.Text);
  //②
  page.SimulateButtonClick();
  //③
  Assert.AreEqual("Hello world", page.messageTextBlock.Text);
}
①の部分でまずはデフォルトのなにも設定されていない状態を検証しています。その場合は値はnullになります。
②の部分でボタンがクリックされたことをシミュレートしています。ですのでSilverlightプロジェクトの中ではボタンがクリックされた場合と、
最後の③の部分で、
それでは実行してみます。実行画面をじっくり見ていただくと一瞬ですがPageに追加したボタンとテキストブロックが実際に表示されていることも確認できます。
簡単にまとめるとUIのテストは以下のような流れで作成することになります。
- キーボードやマウスの操作が行われた時の処理を記述
 - Internalで上記の操作を呼ぶシミュレート用のメソッドを記述
 - テストクラスより上記のシミュレート用のメソッドを呼ぶテストメソッドを記述
 
非同期のテスト
前編・
まずは非同期検証用に以下のメソッドをPageTestクラスに追加します。
[TestMethod]
[Asynchronous]  //①
public void 非同期処理のテスト()
{
}
非同期のテストには①のAsynchronous属性を設定します。この属性が設定されていないとテストは正常に実行されません。
非同期処理自体については今回はテストメソッドの中にBackgroundWorkerを使って実装します。以下のコードを先ほど追加した[非同期処理のテスト]メソッドの中に追加します。
var worker = new BackgroundWorker();
var result = false; //①
var finishedWork = false; //②
worker.DoWork += (s, e) => //③
{
  e.Result = true;
};
worker.RunWorkerCompleted += (s, e) => //④
{
  result = (bool)e.Result;
  finishedWork = true;
};
まず、
③のBackgroundWorkerのDoWorkに設定された匿名デリゲートはBackgrondWorkerが実行された時に呼び出されます。この中で処理結果に true を設定しています。最後にこの処理結果をテストで判定します。
④のBackgroundWorkerのRunWorkerCompletedに設定された匿名デリゲートはBackgrondWorkerの処理が完了した時に実行されます。ここでは処理の結果をテストで検証するresult変数に代入し、
では、
this.EnqueueCallback(() => worker.RunWorkerAsync()); //①
this.EnqueueConditional(() => finishedWork); //②
this.EnqueueCallback(() => Assert.IsTrue(result)); //③
this.EnqueueTestComplete(); //④
これらのSilverlightTestクラスがもっている、 EnqueueXXXXメソッドが非同期処理のテスト用のヘルパーメソッドになります。それぞれに設定されたデリゲートはテストメソッドの実行が終了した後に、
まず①でEnqueueCallbackにBackgroundWorkerの処理を実行するデリゲートを設定しています。ここで非同期処理を実行します。テストメソッドの実行が終了した後に、
次に②でEnqueueConditionalメソッドにfinishedWork変数の値が戻り値になるデリゲートを設定しています。EnqueueConditionalメソッドは戻り値の値がtrueになるまで繰り返し、
③ではEnqueueCallbackにアサートを実行するデリゲートを設定しています。ここでresult変数の値がtrueであればテストが成功になります。
最後に④でテストが終了したことを設定しています。
ではテストを実行してみてください。正常に実行されるとおもいます。非同期処理のテストはデバックなしで実行を行ってください。デバックが有効になっている場合、
非同期のテストはいったんデリゲートという形で処理をキューに貯めてから実行されます。大きな流れとしては以下の順番で処理をキューに設定することになります。
- EnqueueCallbackで非同期処理の開始
 - EnqueueConditionalで非同期処理の終了を待機
 - EnqueueCallbackで非同期処理の結果を検討
 - EnqueueTestCompleteで終了
 
次回予告
後編の今回は前編に引き続き、
次回は、