2010-07-08 21 views
5

yo estaba tratando de configurar una unidad de prueba para una clase interna privada, pero tuvo muy poco éxito:Cómo crear una instancia de PrivateType de clase privada interna

namespace Stats.Model 
{ 
    public class DailyStat 
    { 
    private class DailyStatKey // The one to test 
    { 
     private DateTime date; 
     public DateTime Date 
     { 
     get { return date; } 
     set { date = value.Date; } 
     } 

     public StatType Type { get; set; } 

     public override int GetHashCode() 
     { 
     return Date.Year * 1000000 + 
       Date.Month * 10000 + 
       Date.Day * 100 + 
       (int)Type; 
     } 

     public override bool Equals(object obj) 
     { 
     DailyStatKey otherKey = obj as DailyStatKey; 
     if (otherKey == null) 
      return false; 
     return (this.Date == otherKey.Date && this.StatType == otherKey.StatType); 
     } 
    } 
    } 
} 

Probé este código:

PrivateType statKeyType = new PrivateType("Stats.Model", "Stats.Model.DailyStat.DailyStatKey"); 

así como

PrivateType statKeyType = new PrivateType("Stats.Model", "DailyStat.DailyStatKey"); 

En vano.

El nombre de la asamblea es "Stats.Model", y para mí el nombre del tipo parece correcto también, pero me acaba de obtener una excepción: "System.TypeLoadException: No se pudo cargar el tipo"

Entonces, ¿qué estoy haciendo incorrecto ?

PrivateType, a mi leal saber y entender, se basa en la reflexión, y supongo que es muy útil para este escenario, ya que no se puede tener una clase privada directamente debajo de un espacio de nombres.

EDIT:

Añadido aplicación plena de DailyStatKey. Lo que quiero probar es la singularidad de mi método GetHashCode. Como puede ver, trato de ajustar una fecha + tipo en una sola int.

+0

En C#, "new SomeType (args)" no usa Reflection. En cambio, siempre es una llamada de constructor y llama a uno de los constructores disponibles. En su código de ejemplo, no hay ningún constructor, por lo que el único constructor disponible es el constructor predeterminado (constructor nulo - constructor sin args). Tampoco puedes llamar esto desde el exterior porque la clase es privada. Entonces tienes razón de que necesitas reflexión. – apollodude217

+0

Pregunta relacionada: http: // stackoverflow.com/questions/3198912/use-reflection-or-a-property-when-unit-testing – apollodude217

+1

¿No estoy seguro de por qué menciona la parte sobre constructores? Lo que quiero decir sobre PrivateType usando Reflection, es que lo usa para acceder a la clase/métodos privados. (Esto es bastante obvio desde la página de MSDN: http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.privatetype(VS.80).aspx ya que requiere ReflectionPermission. En cuanto a la pregunta relacionada, sí, soy consciente de que no se deben probar clases privadas, pero dado que solo se usa internamente y es crucial que GetHashCode siempre devuelva algo único, pensé que sería mejor hacer una prueba. – Steffen

Respuesta

5

encontrado una solución a mí mismo:

var parentType = typeof(DailyStat); 
var keyType = parentType.GetNestedType("DailyKeyStat", BindingFlags.NonPublic); 
//edited to use GetNestedType instead of just NestedType 

var privateKeyInstance = new PrivateObject(Activator.CreateInstance(keyType, true)); 

privateKeyInstance.SetProperty("Date", DateTime.Now); 
privateKeyInstance.SetProperty("Type", StatType.Foo); 

var hashCode = (int)privateKeyInstance.Invoke("GetHashCode", null); 
1

Dado que es privado, la única clase que puede crear la instancia es DailyStat. A menos que lo hagas, la reflexión no privada (activador) sería tu única opción si quieres crear la clase, aunque no sería una buena idea, ya que no podrás utilizarla directamente a menos que puedas transmitirla a un público lo suficientemente amplio. tipo o interfaz

EDIT:

Puesto que usted está tratando de hacer esto para las pruebas unitarias entonces efectivamente usted no debe probar esta clase ya que es privado. Solo podrá probarlo a través de cualquier interfaz pública de DailyStat.

+0

Hmm pero PrivateType usa Reflection AFAIK, entonces Todavía no veo por qué es un problema. Además de para qué estaba hecho PrivateType, si no es así? No puede tener una clase privada directamente en un espacio de nombres. – Steffen

+0

Solo puede tener clases internas privadas que sean útiles si desea encapsular la funcionalidad que solo usará la clase externa. Solo puede usar el nuevo PrivateType() dentro de la clase en la que está codificado. No hay refelction involucrado. Mencioné la reflexión ya que se puede usar para instalar clases privadas y llamar a métodos privados fuera de la clase – aqwert

+0

. Es exactamente para la funcionalidad de encapsulado que se usa solemnemente dentro de la clase externa. Entonces esa parte está bien. Comprobaré si PrivateType funciona dentro de la clase externa. – Steffen

-2

Puede codificar un método público "GetDailyStatKey" en la clase principal.

public class DailyStat 
{ 
    private class DailyStatKey // The one to test 
    { 
    } 
    public DailyStatKey GetDailyStatKey() 
    { 
     return new DailyStatKey(); 
    } 
} 

Ahora se puede escribir:

DailyStat v = new DailyStat(); 
var x = v.GetDailyStatKey(); 
+2

Eso es imposible porque el tipo que está devolviendo es invisible para el lado de la llamada. También puedes establecer DailyStatKey en público si quieres esto. – Dykam

+0

Como Dykam dice que no funcionará, además podría ir con una clase interna pública, lo cual realmente no quiero. – Steffen

+0

Puede poner una interfaz en la clase interna que sea pública para que pueda controlar lo que es visible para el mundo – aqwert

3

También puede utilizar PrivateType directamente así:

PrivateType statKeyType = new PrivateType("Stats.Model", "Stats.Model.DailyStat+DailyStatKey"); 

clases anidadas tienen un formato de cadena que es diferente de su namespace (que es Stats.Model.DailyStat.DailyStatKey) por lo que el uso no es obvio.

Cuestiones relacionadas