2011-03-22 24 views
22

Estoy probando una clase que es parte de una jerarquía. He estado configurando mis clases de prueba con el objeto bajo prueba, y un PrivateObject para permitir el acceso a ese objeto. Recibo excepciones cuando intento acceder a miembros privados de la clase principal.¿Cómo puedo utilizar PrivateObject para acceder a miembros privados de mi clase y su principal?

La única solución que he encontrado hasta ahora es pasar un PrivateType especificando la clase base al constructor PrivateObject, pero luego no funciona en los miembros privados de la subclase.

¿Hay alguna manera de que pueda hacer esto, tal vez mediante el uso del parámetro de banderas de enlace en los métodos Get * del objeto privado?

Intenté utilizar las clases de Accessor generadas automáticamente (clic con el botón derecho en la clase principal, Crear intérprete privado). Sin embargo, eso es peor: muestra una propiedad que puedo leer, pero arroja la misma excepción que PrivateObject, y no hay otras opciones que pueda usar (banderas de encuadernación o lo que sea) para arreglar la excepción.

Aquí está mi código de prueba de muestra. Me gustaría que haya alguna forma de construir y usar PrivateObject para recuperar ambos campos.

public class BaseClass 
{ 
    private int one = 1; 
} 

public class SubClass : BaseClass 
{ 
    private int two = 2; 
} 

[TestClass] 
public class UnitTest1 
{ 
    BindingFlags flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; 

    [TestMethod] 
    public void TestMethod1() 
    { 
     SubClass test = new SubClass(); 
     PrivateObject priv = new PrivateObject(test); 

     Assert.AreNotEqual<int>(0, (int)priv.GetFieldOrProperty("one", flags)); // System.MissingMethodException: Method 'PrivateObjectTester.SubClass.one' not found. 
     Assert.AreNotEqual<int>(0, (int)priv.GetFieldOrProperty("two", flags)); 
    } 

    [TestMethod] 
    public void TestMethod2() 
    { 
     SubClass test = new SubClass(); 
     PrivateObject priv = new PrivateObject(test, new PrivateType(typeof(BaseClass))); 

     Assert.AreNotEqual<int>(0, (int)priv.GetFieldOrProperty("one", flags)); 
     Assert.AreNotEqual<int>(0, (int)priv.GetFieldOrProperty("two", flags)); // System.MissingMethodException: Method 'PrivateObjectTester.BaseClass.two' not found. 
    } 
} 
+1

Como nota al margen, puede acceder a los miembros protegidos utilizando PrivateObject –

Respuesta

28

No encontré la respuesta, así que esto es lo que terminé haciendo. Creé PrivateObjects para cada nivel de la jerarquía de la clase, y solo tengo que tener cuidado al escribir casos de prueba que uso el correcto.

public class BaseClass 
{ 
    private int one = 1; 
} 

public class SubClass : BaseClass 
{ 
    private int two = 2; 
} 

[TestClass] 
public class UnitTest1 
{ 
    [TestMethod] 
    public void TestMethod() 
    { 
     SubClass test = new SubClass(); 
     PrivateObject privSub = new PrivateObject(test, new PrivateType(typeof(SubClass))); 
     PrivateObject privBase = new PrivateObject(test, new PrivateType(typeof(BaseClass))); 

     Assert.AreNotEqual<int>(0, (int)privBase.GetFieldOrProperty("one")); 
     Assert.AreNotEqual<int>(0, (int)privSub.GetFieldOrProperty("two")); 
    } 
} 
+0

Esta es la forma correcta de hacerlo. – Thought

8

Probablemente esta no sea la respuesta que desea ... pero no debería probar ambas clases en un método en primer lugar. Solo deberías probar una clase a la vez. Si siente la necesidad de hacer esto, entonces supongo que su código necesita refactorización. Pero como no conozco el problema del código de la vida real, no puedo decir con certeza

+2

No estoy realmente probando dos clases, estoy probando una clase, junto con su clase base.En mi caso, estoy probando algún comportamiento de limpieza: cuando el objeto se purifica, estoy comprobando que todas las tareas de limpieza se han realizado correctamente, y algunas están en la base, y algunas en la subclase. Tiene razón, idealmente, debería estar probando la limpieza de la clase base por sí mismo, pero parte de esta prueba está probando una limpieza anormal que solo puede ocurrir con esta subclase en particular. –

1

PrivateObject es un poco "tonto" a la hora de gestionar la herencia. Lamentablemente, sus métodos no son virtuales, por lo que no hay una manera fácil de cambiar este comportamiento. Básicamente, tiene dos opciones: vivir con las limitaciones o crear su propio asistente de acceso privado que puede manejar los miembros heredados de forma transparente.

1
// create an instance of class SearchPlanogramsBuilder: 
SearchPlanogramsBuilder searchPlanogramBuilder = new SearchPlanogramsBuilder(); 

// executing the method BuildSearchParameters(return type is void) with input searchPlanoGramsFilters: 
searchPlanogramBuilder.BuildSearchParameters(searchPlanoGramsFilters); 

// create privateobject and pass instance created for the class: 
PrivateObject helperobject1 = new PrivateObject(searchPlanogramBuilder); 

// type cast exactly as parameter(which is private variable) in the method: 
Collection<string> parameter = (Collection<string>)helperobject1.GetFieldOrProperty("parameters"); 
1

Como escribió André Pena. ¿Por qué le gustaría probar private miembros de la Baseclass a través de la Subclase? Tampoco tendrías acceso a estos miembros en el código normal de tu Subclase. Debe hacer que los miembros de Propiedades protected tengan acceso a ellos desde la Subclase.

Luego también puede probar estos Miembros con PrivateObject.

+0

Considere que está escribiendo pruebas de funcionalidad pública que se encuentran en una clase abstracta. Para probar la clase, creas una clase falsa derivada que puedes instanciar. En ese caso, es posible que desee seguir buscando los valores de las variables privadas (digamos una propiedad a la que se vinculó la interfaz de usuario, que no tiene ningún setter público/protegido). No estoy de acuerdo con que el cambio de los miembros para acomodar la facilidad de las pruebas sea la respuesta. – kodjeff1

1

Quería hacer lo mismo e hice estos métodos de extensión. Ahora funciona bien Mi idea inicial es tu publicación. ¡Gracias!

https://github.com/cactuaroid/PrivateObjectExtensions

lo que está haciendo básicamente es encontrar dueño del miembro por Type.GetFields()Type.GetProperties() y de forma recursiva, y luego crear PrivateObject (o PrivateType) como tipo correcto para acceder al miembro.

Cuestiones relacionadas