Archivo

Archivo para Octubre, 2008

Silverlight Toolkit de pruebas unitarias

30 de octubre 2008 No hay comentarios

Introducción

Unidad de prueba es muy importante para el desarrollo de software de calidad. El kit de herramientas de Silverlight tiene pruebas exhaustivas de la unidad, así como muestras de las buenas. Silverlight Toolkit proyectos de pruebas unitarias (Controls.Testing, Controls.Test.DataVisualization, Controls.Testing.Theming) utilizar la Unidad de Silverlight Test Framework (Microsoft.Silverlight.Testing.dll y Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight.dll) inventado por Jeff Wilcox , y una biblioteca de prueba de la unidad de la clase (Controls.Testing.Common.dll) inventado por Ted Glaza. Usted puede encontrar mucha información útil acerca de Marco de Silverlight Unidad de Prueba de su sitio de MSDN Code Gallery y el blog de ​​Jeff . Este post sobre todo habla de la biblioteca de clases de Ted unidad de prueba lanzado en el kit de herramientas de Silverlight , y demuestra, con ejemplos de cómo nuestras pruebas unitarias se construyen en la parte superior de ese marco.

A continuación se muestra un diagrama de clases simplificado, que muestra algunas de las clases en el proyecto Controls.Testing.Common, y el patrón de diseño global de nuestras pruebas de la unidad:

Silverlight Toolkit Unit Test Class Diagram

Figura 1: Diagrama de clases Controls.Testing.Common

Jerarquías paralelas de clase

Es bastante claro en el diagrama de clase por encima de que hay jerarquías paralelas de la clase:

  1. FrameworkElement <- Control <- ContentControl
  2. FrameworkElementTest <- ControlTest <- ContentControlTest
  3. IOverriddenFrameworkElement <- IOverriddenControl <- IOverriddenContentControl

La primera jerarquía es la cadena de herencia de las clases de control de la unidad está a prueba.

La segunda jerarquía es la cadena de herencia de las correspondientes clases de pruebas de unidades, en paralelo con las clases de control que se están probando. La razón de este diseño es que si ContentControl es un control, entonces ContentControlTest debe probar todo ControlTest hace. Hay algunos patrones comunes en el diseño de prueba de la clase. Suponiendo que hereda XXX de YYY:

  • XXXTest hereda de YYYTest. Si XXXTest no es abstracto, que está marcado con [TestClass] atributo.
      [TestClass]
     HeaderedContentControlTest: public partial class ExpanderTest
     { 

  • XXXTest introduce tres nuevas propiedades: DefaultXXXToTest, XXXsToTest, OverriddenXXXsToTest, y los utilizan para poner en práctica la anulación de las tres propiedades (DefaultYYYToTest, YYYsToTest, OverriddenYYYsToTest) introducidas por su base de clase YYYTest:
     public override HeaderedContentControl DefaultHeaderedContentControlToTest { get { return DefaultExpanderToTest; } } /// <summary> /// Gets instances of HeaderedContentControl (or derived types) to test. /// </summary> public override IEnumerable<HeaderedContentControl> HeaderedContentControlsToTest { get { return ExpandersToTest.OfType<HeaderedContentControl>(); } } /// <summary> /// Gets instances of IOverriddenContentControl (or derived types) to test. /// </summary> public override IEnumerable<IOverriddenHeaderedContentControl> OverriddenHeaderedContentControlsToTest { get { return OverriddenExpandersToTest.OfType<IOverriddenHeaderedContentControl>(); } } #endregion HeaderedContentControls to test #region Expanders to test /// <summary> /// Gets a default instance of Expander (or a derived type) to test. /// </summary> public virtual Expander DefaultExpanderToTest { get { return new Expander(); } } /// <summary> /// Gets instances of Expander (or derived types) to test. /// </summary> public virtual IEnumerable<Expander> ExpandersToTest { get { yield return DefaultExpanderToTest; for ( int i = 0; i < 4; i++) { Expander expander = new Expander { ExpandDirection = (ExpandDirection)i, IsExpanded = (i % 2 == 0) }; yield return expander; } } } /// <summary> /// Gets instances of IOverriddenContentControl (or derived types) to test. /// </summary> public virtual IEnumerable<IOverriddenExpander> OverriddenExpandersToTest { get { yield break ; } } #endregion Expanders to test # HeaderedContentControls región para poner a prueba / / / <summary> / / / Obtiene una instancia predeterminada de HeaderedContentControl (o un tipo derivado) para poner a prueba / / / </ summary> public override HeaderedContentControl DefaultHeaderedContentControlToTest {get {return DefaultExpanderToTest;}} / /. / <summary> / / / Obtiene los casos de HeaderedContentControl (o tipos derivados) para poner a prueba / / / </ summary> public override IEnumerable HeaderedContentControlsToTest <HeaderedContentControl>. {get {return ExpandersToTest.OfType <HeaderedContentControl> ();}} / / / <summary> / / / Obtiene los casos de IOverriddenContentControl (o tipos derivados) para poner a prueba / / / </ summary> public override IEnumerable OverriddenHeaderedContentControlsToTest <IOverriddenHeaderedContentControl>. {get {return OverriddenExpandersToTest.OfType <IOverriddenHeaderedContentControl> ();}} # endregion HeaderedContentControls para probar # region expansores para poner a prueba / / / <summary> / / / Obtiene una instancia predeterminada de expansor (o un tipo derivado) para probar. / / / </ summary> public Virtual Expander DefaultExpanderToTest {get {return nueva Expander ( );}} / / / <summary> / / / Obtiene los casos de ampliación (o tipos derivados) para poner a prueba / / / </ summary> public virtual de IEnumerable <Expander> ExpandersToTest {get {rendir DefaultExpanderToTest retorno;. for (int i = 0; i <4; i + +) {expansor expansor expansor = new {ExpandDirection = (ExpandDirection) i, = IsExpanded (i% 2 == 0)}; expansor yield return;}}} / / / <summary> / / / Obtiene los casos de IOverriddenContentControl (o tipos derivados) para poner a prueba / / / </ summary> public virtual de IEnumerable <IOverriddenExpander> OverriddenExpandersToTest {get {break rendimiento;}}. # endregion expansores para poner a prueba 

  • XXXTest tiene un constructor público, y anula GetDependencyPropertyTest método:
     IEnumerable<DependencyPropertyTestMethod> GetDependencyPropertyTests() { IList<DependencyPropertyTestMethod> tests = TagInherited( base .GetDependencyPropertyTests()); / / / <summary> / / / Obtener los ensayos de propiedades de dependencia. / / / </ Summary> / / / <returns> Las pruebas de propiedad de dependencia.> </ Retornos públicos GetDependencyPropertyTests anulación IEnumerable <DependencyPropertyTestMethod> () {IList <DependencyPropertyTestMethod > Pruebas = (. TagInherited base de GetDependencyPropertyTests ()); 

  • XXXTest mayo anula los métodos y TemplatePartsAreDefined TemplateVisualStateAreDefined, si XXX tiene contrato nuevo control se define o modifica contrato de sus antepasados ​​contrato:
     [TestMethod] [Description( "Verifies the Control's TemplateParts." )] public override void TemplatePartsAreDefined() { IDictionary< string , Type> templateParts = DefaultControlToTest.GetType().GetTemplateParts(); Assert.AreEqual(1, templateParts.Count); Assert.AreSame( typeof (ToggleButton), templateParts[ "ExpanderButton" ]); } /// <summary> /// Verify the control's template visual states. /// </summary> [TestMethod] [Description( "Verify the control's template visual states." )] public override void TemplateVisualStatesAreDefined() { IDictionary< string , string > visualStates = DefaultControlToTest.GetType().GetVisualStates(); Assert.AreEqual(12, visualStates.Count); Assert.AreEqual< string >( "CommonStates" , visualStates[ "Normal" ]); Assert.AreEqual< string >( "CommonStates" , visualStates[ "MouseOver" ]); Assert.AreEqual< string >( "CommonStates" , visualStates[ "Pressed" ]); Assert.AreEqual< string >( "CommonStates" , visualStates[ "Disabled" ]); Assert.AreEqual< string >( "FocusStates" , visualStates[ "Focused" ]); Assert.AreEqual< string >( "FocusStates" , visualStates[ "Unfocused" ]); Assert.AreEqual< string >( "ExpansionStates" , visualStates[ "Expanded" ]); Assert.AreEqual< string >( "ExpansionStates" , visualStates[ "Collapsed" ]); Assert.AreEqual< string >( "ExpandDirectionStates" , visualStates[ "ExpandDown" ]); Assert.AreEqual< string >( "ExpandDirectionStates" , visualStates[ "ExpandUp" ]); Assert.AreEqual< string >( "ExpandDirectionStates" , visualStates[ "ExpandLeft" ]); Assert.AreEqual< string >( "ExpandDirectionStates" , visualStates[ "ExpandRight" ]); } #endregion Control contract # Región de control del contrato / / / <summary> / / / Verifica TemplateParts del control. / / / </ Summary> [TestMethod] [Description ("Verifica TemplateParts del control.")] Public override void TemplatePartsAreDefined () {IDictionary <cadena de , tipo> = templateParts DefaultControlToTest.GetType () GetTemplateParts ();. Assert.AreEqual (1, templateParts.Count); Assert.AreSame (typeof (ToggleButton), templateParts ["ExpanderButton"]);} / / / <summary> / / / Verificar el control de la plantilla de los estados visuales. / / / </ summary> [TestMethod] [Description ("Verifique que el control de la plantilla de los estados visuales.")] public override void TemplateVisualStatesAreDefined () {IDictionary <string, string> visualStates = DefaultControlToTest .. GetType () GetVisualStates (); Assert.AreEqual (12, visualStates.Count); Assert.AreEqual <> string ("CommonStates", visualStates ["Normal"]); Assert.AreEqual <> string ("CommonStates", visualStates ["mouseover"]); Assert.AreEqual <> string ("CommonStates", visualStates ["Presionado"]); Assert.AreEqual <> string ("CommonStates", visualStates ["Disabled"]); Assert.AreEqual < > string ("FocusStates", visualStates ["Centrado"]); Assert.AreEqual <> string ("FocusStates", visualStates ["desenfocado"]); Assert.AreEqual <> string ("ExpansionStates", visualStates ["Ampliación" ]); Assert.AreEqual <> string ("ExpansionStates", visualStates ["colapsado"]); Assert.AreEqual <> string ("ExpandDirectionStates", visualStates ["ExpandDown"]); Assert.AreEqual <> string ("ExpandDirectionStates "visualStates [" ExpandUp "]); Assert.AreEqual <> string (" ExpandDirectionStates ", visualStates [" ExpandLeft "]); Assert.AreEqual <> string (" ExpandDirectionStates ", visualStates [" ExpandRight "]);} # endregion contrato de control 

    Tenga en cuenta que aunque los contratos de control, anotadas con [TemplateVisualState ()] y [TemplatePart ()] atributos, no se heredan a través de la jerarquía de clases en la teoría. En realidad, suelen ser, a través de contrato de control subclase nueva declaración de clase base. Así que nuestras clases de pruebas de unidad de control como el tratamiento de contrato heredado.

La tercera jerarquía es para las pruebas de la interfaz de usuario y el evento:

  / / / <summary>
 / / / Interfaz utilizado para probar los miembros virtuales de Expander.
 / / / </ Summary>
 IOverriddenExpander interfaz pública: IOverriddenHeaderedContentControl
 {
     / / / <summary>
     / / / Obtiene las acciones de prueba OnExpanded.
     / / / </ Summary>
     ExpandedActions OverriddenMethod {get;}
     / / / <summary>
     / / / Obtiene las acciones de prueba OnCollapsed.
     / / / </ Summary>
     CollapsedActions OverriddenMethod {get;}
 } 

En realidad, hay una jerarquía de cuarto curso paralelo que se utiliza generalmente con la tercera jerarquía entre sí:

  1. OverriddenFrameworkElement <- OverriddenControl <- OverriddenContentControl

Todavía no se ha implementado en Controls.Testing.Common, pero tiene clase Controls.Testing OverriddenTreeView, que es un buen ejemplo para mostrar lo que las clases superiores se vería y cómo iban a ser utilizados, si se aplica. Puedo escribir un post aparte para las dos jerarquías de clases se reemplaza, o modificar éste para añadir más cobertura sobre ellos.

TestBase

TestBase
Figura 2: TestBase

  TestBase public abstract class: SilverlightTest
 {
     / / Campos
     [CompilerGenerated]
     private static int <DefaultVisualDelayInMilliseconds> k__BackingField;

     / / Métodos
     TestBase estática ();
     protegida TestBase ();
     visualDelay); protected void EnqueueVisualDelay (visualDelay int);
     Action[] actions); TestAsync protegida vacío interno (elemento FrameworkElement, Acción params [] acciones);
     visualDelay, FrameworkElement element, params Action[] actions); protected internal void TestAsync (int visualDelay, elemento FrameworkElement, Acción params [] acciones);
     Action<T>[] actions) where T: FrameworkElement; protected internal void TestSequenceAsync <T> (IEnumerable elementos <T>, params Acción <T> [] acciones), donde T: FrameworkElement;
     visualDelay, IEnumerable<T> elements, params Action<T>[] actions) where T: FrameworkElement; protected internal void TestSequenceAsync <T> (visualDelay int, IEnumerable elementos <T>, params Acción <T> [] acciones), donde T: FrameworkElement;
     Action[] actions); TestTaskAsync protegida vacío interno (elemento FrameworkElement, Acción params [] acciones);
     visualDelay, FrameworkElement element, params Action[] actions); protected internal void TestTaskAsync (int visualDelay, elemento FrameworkElement, Acción params [] acciones);

     / / Propiedades
     protegidos internos DefaultVisualDelayInMilliseconds static int {[CompilerGenerated] recibe; [CompilerGenerated] set;}
 } 

TestBase envuelve WorkItemTest métodos como EnqueueCallback, EnqueueSleep EnqueueConditional, EnqueueTestcomplete, y ofrecer dos funciones de alto nivel de utilidad y TestAsync TestSequenceAsync. Cada función tiene una sobrecarga que toma un retraso en milisegundos, para dar árbol visual algo de tiempo para hacer que entre las acciones de la prueba.

Propiedad dependiente de pruebas unitarias

Otro patrón claro en la figura 1: Diagrama de Clase Control.Testing.Common es que todas las clases de prueba, utilice DependencyPropertyTest clase genérica <T,P> a la prueba de la unidad de ejecución de las propiedades de dependencia que introducen. Adición de prueba de unidad para la dependencia de la propiedad del PPP de la clase XXX por lo general incluye tres pasos:

  1. Definir la propiedad "DependencyPropertyTest <T,P> PPPProperty" de la clase XXXTest:
     DependencyPropertyTest<Expander, ExpandDirection> ExpandDirectionProperty { get; private set; } . / / / <summary> / / / Obtiene la dependencia ExpandDirection prueba de la propiedad / / / </ summary> protegida DependencyPropertyTest <Expander, ExpandDirection> ExpandDirectionProperty {get; conjunto privado;} 

  2. Crear PPPProperty en el constructor de XXXTest:
     , "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 = new DependencyPropertyTest <Expander, ExpandDirection> (this, "ExpandDirection") {= La propiedad Expander.ExpandDirectionProperty, inicializador = inicializador, DefaultValue = ExpandDirection.Down, OtherValues ​​ExpandDirection = new [] {ExpandDirection.Up, ExpandDirection.Left, ExpandDirection. Derecho}, InvalidValues ​​= new Diccionario <ExpandDirection, <Tipo {{(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. Añadir las pruebas adecuadas para esta propiedad de dependencia en GetDependencyPropertyTests de anulación:
     )); 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); / / ExpandDirectionProperty pruebas tests.Add (ExpandDirectionProperty.CheckDefaultValueTest); tests.Add (ExpandDirectionProperty.ChangeClrSetterTest); tests.Add (ExpandDirectionProperty.ChangeSetValueTest); tests.Add (ExpandDirectionProperty.ClearValueResetsDefaultTest); tests.Add (ExpandDirectionProperty.InvalidValueFailsTest), pruebas de . Añadir (ExpandDirectionProperty.InvalidValueIsIgnoredTest); tests.Add (ExpandDirectionProperty.CanBeStyledTest); tests.Add (ExpandDirectionProperty.TemplateBindTest); tests.Add (ExpandDirectionProperty.ChangesVisualStateTest (ExpandDirection.Down, ExpandDirection.Up, "ExpandUp")); pruebas. Añadir (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); 

También puede añadir / quitar / modificar las pruebas de las propiedades de dependencia heredados de las clases base de GetDependencyPropertyTests de anulación:

  tests.RemoveTests (HeaderProperty.TemplateBindTest);
 tests.Add (HeaderProperty.TemplateBindTest.Bug ("TODO:. Investigar por qué no aquí, pero no para la propiedad Content")); 

El mensaje ya es más larga de lo que esperaba, así que voy a parar aquí por ahora. Espero que esto ayudará a entender nuestro código de prueba de unidad y crear software de calidad.