2009-02-25 9 views
12

Soy muy nuevo en los simulacros y estoy tratando de reemplazar un campo privado con un objeto simulado. Actualmente, la instancia del campo privado se crea en el constructor. Mi código es el siguiente ...¿Cómo me burlo de un campo privado?

public class Cache { 
    private ISnapshot _lastest_snapshot; 

    public ISnapshot LatestSnapshot { 
     get { return this._lastest_snapshot; } 
     private set { this._latest_snapshot = value; } 
    } 

    public Cache() { 
     this.LatestSnapshot = new Snapshot(); 
    } 

    public void Freeze(IUpdates Updates) { 
     ISnapshot _next = this.LastestSnapshot.CreateNext(); 
     _next.FreezeFrom(Updates); 
     this.LastestSnapshot = _next; 
    } 

} 

Lo que estoy tratando de hacer es crear una unidad de prueba que afirma ISnapshot.FreezeFrom(IUpdates) se llama desde dentro Cache.Freeze(IUpdates). Supongo que debería reemplazar el campo privado _latest_snapshot con un objeto simulado (¿tal vez suposición incorrecta?). ¿Cómo voy a hacer eso mientras aún conservo un constructor sin parámetros y no recurro a hacer público el conjunto de LatestSnapshot?

Si me voy por completo a escribir la prueba de la manera equivocada, por favor, sírvase señalar también.

La implementación real de ISnapshot.FreezeFrom llama a una jerarquía de otros métodos con un gráfico de objeto profundo, por lo que no estoy demasiado interesado en afirmar el gráfico de objetos.

Gracias de antemano.

Respuesta

16

Estoy casi citando técnicas de "Working Effectively with Legacy Code":

  1. sub-clase de su clase en una unidad de prueba y reemplaza la variable privado con objeto de burla en ella (mediante la adición de un regulador público o en el constructor) . Probablemente tengas que proteger la variable.
  2. Crea un getter protegido para esta variable privada y anularlo en la subclase de prueba para devolver un objeto simulado en lugar de la variable privada real.
  3. Crea un método de fábrica protegido para crear el objeto ISnapshot y anótalo en la subclase de prueba para devolver una instancia de un objeto simulado en lugar de la real. De esta forma, el constructor obtendrá el valor correcto desde el comienzo.
  4. Parametrize constructor para tomar una instancia de ISnapshot.
4

No creo que tenga que burlarse de las variables de miembros privados. ¿No es la idea de burlarse de que la interfaz pública para un objeto funciona como se esperaba? Las variables privadas son detalles de implementación a los que no se refieren los simulacros.

+4

¿Qué pasa si una clase bajo prueba utiliza un campo privado que representa una base de datos. ¿Cómo me burlo de ese campo de base de datos? – Vanuan

+0

desea conocer el patrón de repositorio, que básicamente oculta el almacén de datos detrás de un conjunto de interfaces para que puedan ser burladas y probadas fácilmente – Jason

+1

Aún así, ¿cómo construye ese repositorio simulado y lo pasa al consumidor? ¿Necesita un setter o un parámetro constructor? – Vanuan

3

No estoy seguro de que pueda hacer eso. Si quiere probar _next, probablemente tendrá que pasarlo como un parámetro y luego en su pase de prueba unitario en un objeto falso que luego puede probar utilizando una Expectativa. Eso es lo que haría si estuviera intentando hacerlo en Moq.

Como un ejemplo de lo que podría tratar de usar el marco Moq:

Mock<ISnapshot> snapshotMock = new Mock<ISnapshot>(); 
snapshotMock.Expect(p => p.FreezeFrom(expectedUpdate)).AtMostOnce(); 
Cache c = new Cache(snapshotMock.Object); 
c.Freeze(expectedUpdate); 

Nota: No he tratado de compilar el código de seguridad. Está ahí para dar un ejemplo de cómo abordaría la solución de esto.

1

Esta respuesta puede ser simple, pero mirando el código, ¿hay alguna forma de que no se llame al ISnapshot.FreezeFrom(IUpdates)? Parece que quieres afirmar algo que siempre será cierto.

Como dice Jason, de burla es para situaciones en las que la clase depende de SomeInterface que hacer es un trabajo, y que desea probar YourClass al margen de que sea puesta en práctica de SomeInterface que realmente utiliza en tiempo de ejecución.

+0

si te gusta, hasta :) – Jason

1

La pregunta es: ¿cuáles son los efectos externos visibles si esto funcionó?

¿Qué sucede con todas esas instantáneas? Una opción podría inicializar la memoria caché con su primera instantánea desde el exterior, por ejemplo, en el constructor. Otro podría ser burlarse de lo que sea que las llamadas de Instantáneas importen fuera del caché. Depende de lo que te importe que pase.

1

Podría ser demasiado tarde para responder. De todos modos. También tuve un problema similar.

public class Model 
{ 
    public ISomeClass XYZ{ 
     get; 
     private set; 
     } 
} 

Necesito establecer el valor de XYZ en mi caso de prueba. Resolví el problema usando este syntex.

Expect.Call(_model.XYZ).Return(new SomeClass()); 
_repository.ReplayAll(); 

En el caso anterior podemos hacerlo de esta manera

Expect.Call(_cache.LatestSnapshot).Return(new Snapshot()); 
_repository.ReplayAll(); 
-2

Encienda caché en una plantilla como se muestra a continuación.

template <typename T=ISnapshot> 
public class Cache { 
    private T _lastest_snapshot; 

    public T LatestSnapshot { 
     get { return this._lastest_snapshot; } 
     private set { this._latest_snapshot = value; } 
    } 

    public Cache() { 
     this.LatestSnapshot = new Snapshot(); 
    } 

    public void Freeze(IUpdates Updates) { 
     T _next = this.LastestSnapshot.CreateNext(); 
     _next.FreezeFrom(Updates); 
     this.LastestSnapshot = _next; 
    } 

} 

En el código de producción hacen:

Cache<> foo;//OR 
Cache<ISnapshot> bar; 

En el código de prueba Do:

Cache<MockSnapshot> mockFoo; 
+0

idioma incorrecto: C#, no C++ – skolima

0

Es probable que tenga que refactorizar su clase como esta, con el fin de que sea inyectado con una dependencia diferente para ISnapshot. Tu clase seguirá funcionando de la misma manera.

public class Cache { 
private ISnapshot _lastest_snapshot; 

public ISnapshot LatestSnapshot { 
    get { return this._lastest_snapshot; } 
    private set { this._latest_snapshot = value; } 
} 

public Cache() : this (new Snapshot()) { 
} 

public Cache(ISnapshot latestSnapshot) { 
    this.LatestSnapshot = latestSnapshot; 
} 

public void Freeze(IUpdates Updates) { 
    ISnapshot _next = this.LastestSnapshot.CreateNext(); 
    _next.FreezeFrom(Updates); 
    this.LastestSnapshot = _next; 
} 

} 
0

Usted puede simplemente añadir el método "setSnapshot (ISnapshot)" a la caché con su instancia de clase burlado.

También puede agregar un constructor que tome ISnapshot.

Cuestiones relacionadas