ホーム > Silverlightはテスト > Silverlightツールキットのユニットテスト

Silverlightツールキットのユニットテスト

はじめ

ユニットテストは、品質のソフトウェア開発にとって非常に重要です。 Silverlightツールキットは、豊富なユニットテストだけでなく、良いサンプルを持っています。 Silverlightツールキットのユニットテストプロジェクトは、(、Controls.Testing Controls.Test.DataVisualization、Controls.Testing.Theming)使用のSilverlightのユニットテストフレームワークを (Microsoft.Silverlight.Testing.dll &Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight.dll)によって考案ジェフウィルコックス 、そしてテッドGlazaによって発明された単体テストクラスライブラリ(Controls.Testing.Common.dll)。 あなたに関する有用な情報をたくさん見つけることができますSilverlightのユニットテストフレームワークのからMSDNコードギャラリーのサイトJeffのブログを この記事は、主に年にリリースさTedの単体テストクラスライブラリについて語ってSilverlightツールキット 、および私たちのユニットテストは、そのフレームワークの上に構築されている方法の例を示しています。

以下Controls.Testing.Commonプロジェクト内のクラスのいくつか、と私たちのユニットテストの全体的な設計パターンを示す、単純化されたクラス図です:

Silverlight Toolkit Unit Test Class Diagram

図1:Controls.Testing.Commonのクラス図

並列クラス階層

それは、パラレルなクラ​​ス階層があることを上記のクラス図から実にはっきりしている。

  1. FrameworkElementは< - コントロール< - ContentControlを
  2. FrameworkElementTest < - ControlTest < - ContentControlTest
  3. IOverriddenFrameworkElement < - IOverriddenControl < - IOverriddenContentControl

最初の階層では、ユニットテストされているコントロールクラスの継承チェーンです。

第二階層では、テスト対象のコントロールクラスを並列に接続する、対応するユニットテストクラスの継承チェーンです。 この設計のための理由は、ContentControlがコントロールの場合、ContentControlTestはControlTestが行うすべてをテストする必要があることです。 テストクラスの設計にいくつかの一般的なパターンがあります。 YYYからXXXの継承を仮定すると:

  • XXXTestはYYYTestから継承されます。 XXXTestが抽象的でない場合は、[testClassという]属性でマークされています。
      [testClassという]
     公共部分クラス ExpanderTest:HeaderedContentControlTest
     { 

  • XXXTestは、3つの新しいプロパティが導入さ:DefaultXXXToTest、XXXsToTest、OverriddenXXXsToTestを、そしてその基本クラスのYYYTestによって導入された三つのプロパティ(DefaultYYYToTest、YYYsToTest、OverriddenYYYsToTest)のオーバーライドを実装するためにそれらを使用します。
     テストする#地域の HeaderedContentControls
     / / / <summary>
     / / / HeaderedContentControlの既定のインスタンス(または派生型)をテストするために取得します。
     / / / </概要>
     公共オーバーライド HeaderedContentControl DefaultHeaderedContentControlToTest
     {
        取得
         {
             DefaultExpanderToTestを返します
         }
     }  
    
     / / / <summary>
     / / /テストするHeaderedContentControlのインスタンス(または派生型)を取得します。
     / / / </概要>
     公共オーバーライド IEnumerableを<HeaderedContentControl> HeaderedContentControlsToTest
     {
        取得
         {
             ExpandersToTest.OfTypeの<HeaderedContentControl>()を返す
         }
     }  
    
     / / / <summary>
     / / /テストするIOverriddenContentControlのインスタンス(または派生型)を取得します。
     / / / </概要>
     公共オーバーライド IEnumerableを<IOverriddenHeaderedContentControl> OverriddenHeaderedContentControlsToTest
     {
        取得
         {
             OverriddenExpandersToTest.OfTypeの<IOverriddenHeaderedContentControl>()を返す
         }
     }
    テストするを#endregion HeaderedContentControls  
    
    テストする#地域パンダ
     / / / <summary>
     / / /テストするエキスパンダー(または派生型)の既定のインスタンスを取得します。
     / / / </概要>
     公共バーチャルエキスパンダーDefaultExpanderToTest
     {
        取得
         {
             新しいExpander()返す
         }
     }  
    
      / / / <summary>
     / / /テストするためにパンダのインスタンス(または派生型)を取得します。
     / / / </概要>
     パブリック仮想 IEnumerableを<Expander>のExpandersToTest
     {
        取得
         {
             DefaultExpanderToTestを返すもたらす  
    
             i = 0; i < 4; i++) するfor(int i = 0;私<4は、i + +)
             {
                パンダパンダ= 新しいExpander
                 {
                     ExpandDirection =(ExpandDirection)私、
                     IsExpanded =(I%2 == 0)
                 };
                パンダを返し得る
             }
         }
     }  
    
     / / / <summary>
     / / /テストするIOverriddenContentControlのインスタンス(または派生型)を取得します。
     / / / </概要>
     パブリック仮想 IEnumerableを<IOverriddenExpander>のOverriddenExpandersToTest
     {
        取得
         {
             破壊をもたらす
         }
     }
    テストする#endregionパンダ 

  • XXXTestは、publicコンストラクタを持っており、GetDependencyPropertyTestメソッドをオーバーライド:
      / / / <summary>
     / / /依存関係プロパティのテストを取得します。
     / / / </概要>
     / / / <returns>依存関係プロパティのテスト。</リターン>
     公共オーバーライド IEnumerableを<DependencyPropertyTestMethod>のGetDependencyPropertyTests()
     {
         IList内<DependencyPropertyTestMethod>テスト= TagInherited( ベース GetDependencyPropertyTests()); 

  • XXXTest 5月には、XXXは、新しいコントロールコントラクトが定義されている場合、TemplatePartsAreDefinedとTemplateVisualStateAreDefinedメソッドをオーバーライド、またはその祖先に"契約の契約を変更します。
      #地域のコントロール契約
     / / / <summary>
     / / /コントロールのTemplatePartsを検証します。
     / / / </概要>
     [のTestMethod]
     [説明("コントロールのTemplatePartsを検証します。")]
     TemplatePartsAreDefined 公共オーバーライドするvoid()
     {
         IDictionaryを< 文字列 、タイプ> templateParts = DefaultControlToTest.GetType()GetTemplateParts();
         Assert.AreEqual(1、templateParts.Count);
         ]); Assert.AreSame(typeof演算 (トグルボタン)、templateParts ["ExpanderButton"]);
     }  
    
     / / / <summary>
     / / /コントロールのテンプレートのビジュアル状態を確認してください。
     / / / </概要>
     [のTestMethod]
     [説明("コントロールのテンプレートのビジュアル状態を確認してください。")]
     TemplateVisualStatesAreDefined 公共オーバーライドするvoid()
     {
         > visualStates = DefaultControlToTest.GetType().GetVisualStates(); 。IDictionaryを< 文字列文字列 > visualStates = DefaultControlToTest.GetType()GetVisualStates();  
    
         Assert.AreEqual(12、visualStates.Count);  
    
         , visualStates[ "Normal" ]); Assert.AreEqual < 文字列 >("CommonStates"、visualStates ["ノーマル"]);
         , visualStates[ "MouseOver" ]); Assert.AreEqual < 文字列 >("CommonStates"、visualStates ["マウスオーバー"]);
         , visualStates[ "Pressed" ]); Assert.AreEqual < 文字列 >("CommonStates"、visualStates ["押した"]);
         , visualStates[ "Disabled" ]); Assert.AreEqual < 文字列 >("CommonStates"、visualStates ["無効"]);   
    
         , visualStates[ "Focused" ]); Assert.AreEqual < 文字列 >("FocusStates"、visualStates ["集束"]);
         , visualStates[ "Unfocused" ]); Assert.AreEqual < 文字列 >("FocusStates"、visualStates ["フォーカスされていない"]);
         , visualStates[ "Expanded" ]); Assert.AreEqual < 文字列 >("ExpansionStates"、visualStates ["拡張"]);
         , visualStates[ "Collapsed" ]); Assert.AreEqual < 文字列 >("ExpansionStates"、visualStates ["コラ"]);
         , visualStates[ "ExpandDown" ]); Assert.AreEqual < 文字列 >("ExpandDirectionStates"、visualStates ["ExpandDown"]);
         , visualStates[ "ExpandUp" ]); Assert.AreEqual < 文字列 >("ExpandDirectionStates"、visualStates ["ExpandUp"]);
         , visualStates[ "ExpandLeft" ]); Assert.AreEqual < 文字列 >("ExpandDirectionStates"、visualStates ["ExpandLeft"]);
         , visualStates[ "ExpandRight" ]); Assert.AreEqual < 文字列 >("ExpandDirectionStates"、visualStates ["ExpandRight"]);
     }
     を#endregionコントロール契約 

    コントロールの契約が、注釈を付ける[TemplateVisualState()]と[TemplatePart()]属性を、理論的にはクラス階層を経由して継承されていないこと。注意して​​ください 実際には彼らは通常、サブクラスの再宣言する基本クラスのコントロールコントラクトを通じて、です。 継承されるように私たちのユニットテストクラスは、コントロールコントラクトを扱う。

3番目の階層は、UIとイベントのテスト用です。

  / / / <summary>
 パンダの仮想メンバをテストするために使用される/ / /インターフェイス。
 / / / </概要>
 パブリックインターフェイスの IOverriddenExpander:IOverriddenHeaderedContentControl
 {
     / / / <summary>
     / / / OnExpandedテストアクションを取得します。
     / / / </概要>
     OverriddenMethodのExpandedActions {取得;}
     / / / <summary>
     / / / OnCollapsedテストアクションを取得します。
     / / / </概要>
     OverriddenMethodのCollapsedActions {取得;}
 } 

通常一緒に3番目の階層で使用される第4並列クラス階層は、実際に存在する。

  1. OverriddenFrameworkElement < - OverriddenControl < - OverriddenContentControl

それはまだControls.Testing.Commonで実装されますが、Controls.Testingと上記のクラスは次のようになりますし、実装されていればどのように、使用されるかを示す良い例であるOverriddenTreeViewクラスを、持っていません。 私は、オーバーライドした2つのクラス階層の別の記事を書く、またはそれらについてのより多くの範囲を追加するには、この一つを変更することができます。

TestBase

TestBase
図2:TestBase

  パブリック抽象クラス TestBase:SilverlightTest
 {
     / /フィールド
     [CompilerGenerated]
     プライベート静的なint型 <DefaultVisualDelayInMilliseconds> k__BackingField。

     / /メソッド
     静的 TestBase();
     保護された TestBase();
     visualDelay); 保護されたボイドEnqueueVisualDelay(int型 visualDelay);
     Action[] actions); 保護された内部ボイド TestAsync(FrameworkElementの要素は、paramsアクション[]アクション);
     visualDelay, FrameworkElement element, params Action[] actions); 保護された内部ボイドTestAsync(int型 visualDelay、FrameworkElementの要素は、paramsアクション []アクション);
     Action<T>[] actions) where T: FrameworkElement; ここで、T内部の空隙 TestSequenceAsync <T>は(IEnumerableを<T>要素は、paramsアクション<T> []アクション) 保護 :FrameworkElementは。
     visualDelay, IEnumerable<T> elements, params Action<T>[] actions) where T: FrameworkElement; 、FrameworkElementは: 内部ボイド TestSequenceAsync <T>(int型 visualDelay、IEnumerableを<T>要素は、paramsアクション<T> []アクション)Tを保護
     Action[] actions); 保護された内部ボイド TestTaskAsync(FrameworkElementの要素は、paramsアクション[]アクション);
     visualDelay, FrameworkElement element, params Action[] actions); 保護された内部ボイドTestTaskAsync(int型 visualDelay、FrameworkElementの要素は、paramsアクション []アクション);

     / /プロパティ
     保護された内部静的なint型 DefaultVisualDelayInMilliseconds {[CompilerGenerated]取得、[CompilerGenerated]セット;}
 } 

TestBaseは、EnqueueConditional、EnqueueCallback EnqueueSleep、EnqueueTestcompleteのようなWorkItemTestのメソッドをラップし、そして二つの高レベルのユーティリティ機能TestAsyncとTestSequenceAsyncを提供しています。 各関数は、ビジュアルツリーのテストアクションの間にレンダリングするためにいくつかの時間を与えるために、ミリ秒の遅延を受け取るオーバーロードを持っています。

依存プロパティのユニットテスト

図1から別の明確なパターン:Control.Testing.Commonのクラス図は、すべてのテストクラスは、彼らが導入する依存関係プロパティの実装、ユニットテストにDependencyPropertyTest <T,P>のジェネリッククラスを使用することです。 クラスXXXの依存関係プロパティPPPのためのユニットテストを追加することで、通常3つのステップが含まれています。

  1. クラスXXXTestのプロパティ"DependencyPropertyTest <T,P> PPPProperty"を定義します。
      / / / <summary>
     / / / ExpandDirectionの依存関係プロパティのテストを取得します。
     / / / </概要>
     set; } 保護 DependencyPropertyTest <Expander, ExpandDirection> ExpandDirectionProperty {取得、 プライベートセット;} 

  2. XXXTestのコンストラクタでPPPPropertyを作成します。
     , "ExpandDirection" ) { Property = Expander.ExpandDirectionProperty, Initializer = initializer, DefaultValue = ExpandDirection.Down, OtherValues = new ExpandDirection[] { ExpandDirection.Up, ExpandDirection.Left, ExpandDirection.Right }, InvalidValues = new Dictionary<ExpandDirection, Type> { { (ExpandDirection)(-1), typeof (ArgumentException) }, { (ExpandDirection)4, typeof (ArgumentException) }, { (ExpandDirection)5, typeof (ArgumentException) }, { (ExpandDirection)500, typeof (ArgumentException) }, { (ExpandDirection) int .MaxValue, typeof (ArgumentException) }, { (ExpandDirection) int .MinValue, typeof (ArgumentException) } } }; ExpandDirectionProperty = 新しい DependencyPropertyTest <Expander, ExpandDirection>( この 、"ExpandDirection"){プロパティ= Expander.ExpandDirectionProperty、イニ=初期化子と、DefaultValue = ExpandDirection.Down、OtherValues ​​= 新しい ExpandDirection [] {ExpandDirection.Up、ExpandDirection.Left、ExpandDirection。右}、InvalidValues ​​= 新しい辞書<ExpandDirection, Type>に{{(ExpandDirection)(-1)、typeof演算 (ArgumentExceptionが)}、{(ExpandDirection)4、typeof演算 (ArgumentExceptionが)}、{(ExpandDirection)5、typeof演算 (ArgumentExceptionが) }、{(ExpandDirection)500、typeof演算 (ArgumentExceptionが)}、{(ExpandDirection)int型 MaxValueに、typeof演算 (ArgumentExceptionが)}、{(ExpandDirection)int型MinValue、typeof演算 (ArgumentExceptionが)}}}。。。 

  3. GetDependencyPropertyTestsオーバーライドでこの依存関係プロパティの適切なテストを追加します。
      / / ExpandDirectionPropertyテスト
     tests.Add(ExpandDirectionProperty.CheckDefaultValueTest);
     tests.Add(ExpandDirectionProperty.ChangeClrSetterTest);
     tests.Add(ExpandDirectionProperty.ChangeSetValueTest);
     tests.Add(ExpandDirectionProperty.ClearVa​​lueResetsDefaultTest);
     tests.Add(ExpandDirectionProperty.InvalidValueFailsTest);
     tests.Add(ExpandDirectionProperty.InvalidValueIsIgnoredTest);
     tests.Add(ExpandDirectionProperty.CanBeStyledTest);
     tests.Add(ExpandDirectionProperty.TemplateBindTest);
     tests.Add(ExpandDirectionProperty.ChangesVisualStateTest(ExpandDirection.Down、ExpandDirection.Up、"ExpandUp"));
     tests.Add(ExpandDirectionProperty.ChangesVisualStateTest(ExpandDirection.Up、ExpandDirection.Left、"ExpandLeft"));
     tests.Add(ExpandDirectionProperty.ChangesVisualStateTest(ExpandDirection.Left、ExpandDirection.Right、"ExpandRight"));
     tests.Add(ExpandDirectionProperty.ChangesVisualStateTest(ExpandDirection.Right、ExpandDirection.Down、"ExpandDown"));
     tests.Add(ExpandDirectionProperty.SetXamlAttributeTest);
     tests.Add(ExpandDirectionProperty.SetXamlElementTest); 

また、追加/ GetDependencyPropertyTestsオーバーライドで基本クラスから継承された依存関係プロパティのためにテストを変更する/削除することができます。

  tests.RemoveTests(HeaderProperty.TemplateBindTest);
 tests.Add(HeaderProperty.TemplateBindTest.Bug("TODO:これはContentプロパティのために、ここではなく、失敗した原因を調査してください")); 

ポストはすでに私が予想以上に長いので、私は今のところここに停止します。 これは私たちのユニットテストのコードを理解し、品質のソフトウェアを作成する一助となることを望んでいます。

  1. コメントはまだありません。
  1. トラックバックはまだありません。