2009-07-17 31 views
23

Después de varios años siguiendo la mala práctica heredada de los "arquitectos" en mi lugar de trabajo y pensando que debe haber una mejor manera, recientemente he estado leyendo sobre TDD y DDD y yo Creo que los principios y las prácticas encajarían perfectamente en la complejidad del software que escribimos.TDD, DDD y encapsulación

Sin embargo, muchas de las muestras de TDD que he visto llaman un método en el objeto de dominio y luego prueban las propiedades del objeto para garantizar que el comportamiento se ejecute correctamente.

Por otro lado, varias personas respetadas en la industria (Greg Young, lo que es más notable con sus charlas sobre CQRS) abogan por encapsular completamente cada objeto de dominio mediante la eliminación de todos los 'captadores'.

Mi pregunta por lo tanto es: ¿Cómo se prueba la funcionalidad de un objeto de dominio si está prohibido recuperar su estado?

Creo que me estoy perdiendo algo fundamental así que por favor siéntete libre de llamarme idiota y aclararme, cualquier orientación sería muy apreciada.

+1

Hah, quería leer un poco más sobre este principio "sin captación" después de leer esta publicación ... y esta publicación fue el primer resultado de google. –

+0

Supongo que estaba buscando un nombre para el patrón que había visto discutido en otra parte. Inicialmente, vi un video sobre la separación de consultas de comandos que abogaba por un dominio de solo escritura y una ruta alternativa para consultar el almacén de datos. Luego, leí algunos artículos más en los que se discutían los captadores que violaban la encapsulación, etc. ¿Es posible que esos términos produzcan mejores resultados de búsqueda? – Justin

+0

De su comentario, creo que es probable que lo que escuchó fue "no-establecedores". Mucha gente piensa que los setters violan la encapsulación.De hecho, estoy de acuerdo en general en que se usan en exceso, pero siguen siendo útiles y necesarios en muchos casos. –

Respuesta

17

Lo que está describiendo es verificación de estado en el que se confirma el estado del objeto de dominio. Hay una rama de TDD que se llama , verificación de comportamiento que utiliza objetos simulados.

La verificación del comportamiento le permite especificar qué métodos deben invocarse y, si lo desea, a qué métodos no se llama.

Consulte este artículo de Martin Fowler para más detalles: Mocks Aren't Stubs.

+0

Impresionante - ¡eso me ha dado muchas palabras clave para buscar y carga más material de lectura! Gracias por la gran y rápida respuesta. – Justin

+0

Lea sobre Martin Fowler - ha escrito mucho sobre el tema y es un gran recurso. También encontré Desarrollo basado en pruebas por ejemplo - Escrito por Kent Beck para ser un buen manual en TDD –

+0

Estaba considerando el libro Kent Beck, pero algunas de las revisiones que he visto (especialmente Amazon) no son tan favorables. ¿Es definitivamente uno que recomendarías? – Justin

2

Un par de cosas.

Primero, cuando haces cosas como TDD para hacer que tu código sea comprobable terminas con una clase más pequeña. Si tiene una clase con muchas propiedades privadas que no puede inspeccionar, existe una buena posibilidad de que se divida en varias clases y se pueda volver más comprobable.

En segundo lugar, la arquitectura OO de la vieja escuela intenta hacer que el software sea seguro mediante el uso de protecciones de idioma para evitar que las cosas estén accesibles. Una arquitectura TDD hace que el software sea más robusto al escribir pruebas que verifican lo que realmente hace el código, poniendo menos énfasis en el uso de construcciones de lenguaje para garantizar lo que el programa no hace.

Por último, verificar una propiedad no es la única forma de validar el código, hizo lo que se suponía que debía hacer. El libro xUnit Design Patterns documenta otros enfoques aquí: http://xunitpatterns.com/Result%20Verification%20Patterns.html

+0

Gracias por la respuesta rápida: el enlace es increíble y estoy comenzando a entender la situación. – Justin

+0

Si vincula el enlace, le encantará el libro (lo hice al menos). –

4

Si realmente va a ir tan lejos como para prohibir la recuperación del estado, entonces estará limitado a pruebas de comportamiento, probablemente a través de un marco de burla como TypeMock, que tiene el poder de rastrear el comportamiento de su objeto. Si puede hacer BDD puro, entonces teóricamente puede afirmar la corrección de todo su sistema simplemente por la forma en que se comporta.

En la práctica, he encontrado que BDD es más frágil en muchos casos que las pruebas con estado. Si bien algunas personas pueden solicitar una determinada teoría, solo funciona si funciona para usted. Las pruebas basadas en el estado todavía representan el 90% de todas las pruebas unitarias que escribimos, y estamos muy conscientes de BDD en nuestro equipo.

Haz lo que funcione mejor para ti.

2

Lo que usted menciona se llama prueba de estado. También hay pruebas de comportamiento. Las técnicas utilizadas para eso son Inyección de dependencia, Inversión de control y burla:

Todos los efectos secundarios de su clase se implementan como invocaciones de método en sus "dependencias", es decir, objetos suministrados desde el exterior, generalmente en el constructor. Luego, en su prueba de unidad, proporciona un objeto falso en lugar de uno real. El objeto falso puede recordar si se llamó a su 'determinado método, y eso es lo que afirma en su prueba.

Existen varios marcos de simulación que automatizan la creación de objetos falsos mediante la generación dinámica de clases que implementan una interfaz determinada. Los más populares son Rhino.Mocks y Moq.

+0

+1 Por la respuesta concisa. ¿Podría explicar por favor proporcionando y ejemplo o una referencia a un ejemplo de cómo IoC se utilizaría de esta manera para rastrear invocaciones de método de un objeto. No está claro si el (los) objeto (s) inyectado (s) estaría (n) rastreando llamadas de métodos externos específicos a otros objetos o si se trata de realizar un seguimiento de las llamadas a métodos internos en el mismo objeto o tal vez en ambos. – jpierson

2

Hola Justin, como tú, recientemente estuve pensando en agregar getters a mi objeto de dominio de solo escritura por el bien de las pruebas unitarias, pero ahora estoy convencido de que estaba equivocado. Suponiendo que haya aceptado la idea de un dominio de solo escritura en primer lugar, entonces si tiene getters en absoluto, está pidiendo problemas. El principio de dominio de solo escritura desearía que activara un evento desde su objeto de dominio, o que leyera desde una proyección que escribió su objeto de dominio, o algo así. Una vez que expones los getters estás comenzando a exponer la "forma" del objeto, y como dice Greg Young, "Los objetos de dominio tienen comportamiento, no forma".

Dicho esto, estoy luchando con su misma pregunta ... ¿cómo se prueba un objeto de dominio de escritura única? Este es mi plan actual: estoy pensando en hacer que mi objeto de dominio active un evento de dominio que diga "estas propiedades cambiaron", y en mi prueba de unidad, me registraré antes de enviar el "comando de edición". Consulte la publicación de Udi Dahan sobre eventos de dominio here, y también vea what Eric Evans says about Domain Events.

1

Me preguntaba lo mismo hasta que finalmente tropecé con los siguientes documentos. Los encontré a ser grandes iniciadores de realizar la verificación de comportamiento, sobre todo el primero proporcionarme varios "momentos aha":

  1. Using Mocks and Tests to Design Role-Based Objects
  2. Mock Roles, Not Objects
+0

enlaces están rotos. Aquí está el PDF para el segundo elemento: http://www.jmock.org/oopsla2004.pdf y aquí está el enlace a la mayor parte del primer elemento: http://msdn.microsoft.com/en-us/magazine/dd882516.aspx – sivabudh

+0

El segundo enlace funciona para mí, y el primero funciona si cambia mockobjects.com a www.mockobjects.com – SCFrench

9

bien, esta respuesta es de un año demasiado tarde ;-)

Pero cuando desee probar modelos CQRS, puede hacer afirmaciones sobre los eventos de dominio disparados en lugar de aserciones sobre el estado de la entidad.

p. Ej. si desea probar si llamar: customer.Rename ("Foo") produce el comportamiento correcto.

En lugar de probar si customer.Name es igual a "foo", más bien prueba si hay un evento CustomerRename pendiente con el valor "Foo" en tu tienda de eventos pendientes. (en su lista de eventos de uow o en su entidad dependiendo de la implementación)