2009-06-08 11 views
5

PreguntaEn TDD y DDD, ¿cómo maneja las propiedades de solo lectura en falsificaciones?

¿Cómo se maneja sólo lectura campos al crear falsificaciones?

Antecedentes

Estoy en las etapas de iniciación del uso de ASP.Net MVC y estoy usando tienda de deportes de Steven Sanderson y Cena del empollón de Scott Gu como ejemplos. Un pequeño problema que acabo de abordar es cómo trabajar con propiedades de solo lectura cuando realizo falsificaciones. Estoy usando LINQToSQL.

Mi interfaz es:

public interface IPersonRespository 
{ 
    Person GetPerson(int id); 
} 

y mi falsa se convierte en

public class FakePersonRepository 
{ 
    public Person GetPerson(int id) 
    { 
     return new Person {id="EMP12345", name="John Doe", age=47, ssn=123-45-6789, totalDrWhoEpisodesWatched=42}; 
    } 
} 

Aquí es mi problema. Los campos id, ssn y totalDrWhoEpisodesWatched son de solo lectura, por lo que el código anterior no funcionará. Sin embargo, no reconozco cómo crear una nueva persona falsa y establecer una propiedad de solo lectura. Estoy seguro de que hay una solución, pero todavía no la he encontrado en mis búsquedas.

Actualización: Herencia + Ocultar propiedad como una posible solución?

Todavía no he decidido una solución firme al problema. No me gusta la idea de modificar mis clases de Dominio con el propósito de crear falsificaciones. Para mí, agregar el marcado a las clases de dominio para hacer pruebas es una forma de acoplamiento adicional: el acoplamiento a la implementación de su prueba. Ahora estoy investigando otra posibilidad, que es crear una clase FakePerson, que hereda de Person, pero oculta las propiedades con nuevas propiedades de lectura y escritura.

public class FakePerson: Person 
{ 
    public new int age { get; set; } 
    public new string ssn { get; set; } 
    public new int totalDrWhoEpisodesWatched { get; set; } 
} 

Hasta el momento, esta solución es la forma en que me estoy inclinando. Rompe el Principio de Sustitución de Liskov, sin embargo, eso no me molesta tanto en un proyecto de prueba. Me encantaría escuchar cualquier crítica y/o comentario sobre esto como una solución.

Ganador: Mock Marcos

Moq parece hacer el trabajo. Mi última solución de ocultar la propiedad a través de la herencia funciona, de hecho, sin embargo, al usar Moq, obtengo un conjunto estandarizado de funcionalidad que es más fácil de mantener. Supongo que otros frameworks simulados tienen esta funcionalidad, pero no los he comprobado. Se dice que Moq es más directo para el comienzo de la escritura falsa, que definitivamente estoy en este momento.

+0

¿Por qué se ajusta para que sea de sólo lectura? –

+0

Está configurado como de solo lectura, porque el usuario no del dominio tiene derecho a modificar las propiedades. Podría ser por una serie de razones, pero a menudo, porque la aplicación no tiene derechos para modificar el campo, ya sea porque es un campo de cálculo/generado automáticamente o debido a las políticas de seguridad de la base de datos. – John

+3

Sobre un tema poco relacionado, recomendaría cambiar los nombres de sus propiedades a PascalCase para cumplir con las convenciones de nombres de .NET. http://msdn.microsoft.com/en-us/library/ms229043(loband).aspx –

Respuesta

6

Considere burlarse del tipo de persona en su examen. Ejemplo de uso de Moq:

var mock = new Mock<Person>(); 
mock.SetupGet(p => p.id).Returns("EMP12345"); 
mock.SetupGet(p => p.ssn).Returns("123-45-6789"); 
mock.SetupGet(p => p.totalDrWhoEpisodesWatched).Returns(42); 
return mock.Object; 

De lo contrario, trata de encontrar la manera de LINQ a SQL establece las propiedades de sólo lectura.

EDITAR: Si intenta lo anterior y lanza una Moq ArgumentException en el SetupGet llamada con el mensaje "Configuración no válida en un miembro no reemplazable: p => p.id", entonces usted necesita para marcar la propiedad como virtual. Esto deberá hacerse para cada propiedad cuyo absorbente desee anular.

En LINQ a SQL, esto se puede hacer en el quirófano diseñador mediante la selección de la propiedad, a continuación, en la ventana de propiedades del conjunto de modificador de la herencia a virtuales.

+0

¿Puede moq bypass leer solo los puntales? Recuerdo que tuve problemas con los simulacros de Rhino. :/ –

+0

¿Se está refiriendo a "campos" de solo lectura, o propiedades sin configuradores de acceso público? En el primer caso, no creo que Moq pueda ayudar. En el último caso, los incubadores no importan: Moq simplemente anula el getter. – mvr

+0

Propiedades de solo lectura (es decir, propiedades con el conjunto). Linq to SQL establece la variable privada en la clase Person, que en este ejemplo sería _id, _ssn y _totalDrWhoEpisodesWatched. – John

1

Solo puede establecer propiedades de solo lectura en el constructor de la clase. El objeto Persona debe tener un constructor que acepte id, ssn y totalDrWhoEpisodesWatched. Por supuesto, si se trata de un objeto generado en linqtosql, es posible que tenga problemas para modificarlo ya que el código se genera automáticamente.

Podría considerar el uso de un objeto mapeado para exponer en su repositorio ... por lo que nunca tendría que usar su objeto linqtosql como modelo.

+0

@ Joel- ¿Considera que la sobrecarga de usar un modelo completamente POCO vale la pena el esfuerzo adicional requerido por el doble mapeo? – RichardOD

+0

No me gusta la idea de agregar ssn y totalDrWhoEpisodesWatched al constructor. Agregar parámetros adicionales que no son realmente necesarios en el programa en ejecución solo para realizar pruebas parece contrario al buen diseño de la API, porque significa hacer público un método que los consumidores no deberían usar (que no sea un consumidor de prueba). ¿Qué quiere decir con un "objeto mapeado"? – John

+0

@RichardOD, a veces :-) @John, por objeto mapeado, me refiero a utilizar un objeto que haya codificado usted mismo (en lugar de confiar en la clase autogenerada que proporciona linq2sql). Por lo tanto, cuando seleccione desde su almacén de datos, lo haría (seleccione MyPerson() {...} nuevo y establezca sus propiedades allí. Esto le permite evitar la dependencia de una clase autogenerada que puede o no cambiar en el futuro. –

1

En .NET, puede marcar sus ajustadores como "internos" y usar el atributo de ensamblaje InternsalVisibleTo para hacer que las partes internas sean visibles para su ensamblaje de prueba. De esta forma, sus incubadores no serán públicos, pero aún puede acceder a ellos.

nota: aunque la pregunta no está etiquetada.NET, supuse que se basaba en el uso que usted hace de la sintaxis del inicializador de objetos. Si mi suposición era incorrecta, esta sugerencia no se aplica (a menos que el lenguaje que está utilizando tenga una función equivalente, por supuesto).

+0

No lo marqué como .Net, sin embargo, pensé que las menciones de ASP.net y LinqToSQL me delatarían. Gracias. – John

0

Si es para pruebas, considere usar el reflejo. Eso no implicaría meterse con su modelo de dominio.

Por ejemplo, obtuve la clase FactoryBase, que usa la reflexión para establecer el apoyo necesario mediante la expresión lambda a través de parámetros (como this). Funciona como un encanto: crear una fábrica nueva es simple como definir el tipo de depósito y los datos de entidad predeterminados.

+0

La respuesta es intrigante, pero no veo cómo soluciona el problema sobre el que pregunté, que es cómo manejar las propiedades de solo lectura en falsificaciones. Foo tiene instaladores para las 3 propiedades (Bar, Baz y Bling). ¿Qué harías si BAZ solo tuviera un getter? – John

+0

Reflection puede manejar eso. Obtuve algunos objetos de dominio con instaladores privados y protegidos. –

0

También uso Moq. Me encanta y funciona genial. Pero, antes de comenzar a usar Moq, escribí muchas falsificaciones. Así es como habría resuelto el problema usando falsificaciones.

Dado que un falso puede tener métodos adicionales que la implementación de "producción" no tiene, agregaría algunos métodos adicionales a mi implementación falsa para manejar la configuración de la porción de solo lectura.

De esta manera:

public class FakePersonRepository : IPersonRespository 
{ 
    private IDictionary<int, Person> _people = new Dictionary<int, Person>(); 

    public Person GetPerson(int id) // Interface Implementation 
    { 
     return _people(id); 
    } 

    public void SetPerson(int id, Person person) // Not part of interface 
    { 
     _people.Add(id, person); 
    } 

} 
Cuestiones relacionadas