Entre los muchos pseudo-ides que orbitan TDD, hay un poco de desprecio por los dobles de prueba, no menos relacionados con sus nombres ridículos.
Los llamarían orgullosos de alguna manera, de lo contrario, falsos, trozos, falsos, una sensación de que realmente no perdemos nada si no los usamos. (En contraste con las "pruebas de integración" y las "dependencias reales").
Sin embargo, puede cambiar el punto de vista. Al final, el simulacro no solo y no tanto admite el componente dependiente, sino que especifica el comportamiento de la dependencia. Y la implementación "real" es una encarnación actual, posiblemente errónea de nuestra orgullosa idea.
En este sentido, cada vez que escribimos
when(mockDependency.method(inputValue)).thenReturn(returnValue)
,
documentamos el comportamiento de algunos componentes.
Por lo tanto, hay varias consecuencias.
Los objetos simulados están estrechamente relacionados con las especificaciones de prueba reales, tanto en vocabulario como en significado. Tanto eso como otro nos enfoca en los requisitos y escenarios, pero no en las características de la implementación actual.
El hecho de que las declaraciones sobre un componente que se debe mojar cuando se usan marcos como Mockito nos distraen un poco está disperso entre las clases de clientes, y es difícil hacer un seguimiento de ellas. En este sentido, las clases descendientes explícitas con un comportamiento predefinido son preferibles a simulacros como Mockito, ya que le permiten encapsular scripts y verlos de un vistazo.
Por lo tanto, las clases de script ValidOrderProviderStub, ExpiredOrderProviderStub, InvalidOrderIdException_OrderProviderStub, OnlyOnceOrderProvider, etc., que pueden estar en el mismo paquete y ser utilizadas por todas las pruebas a la vez, se agregan a cada clase OrderProvider.
Esto incluye clases de espías que son fáciles de implementar usted mismo.
Además, este enfoque es más rápido de ejecutar y funciona sin usar la reflexión.
Por lo tanto, al leer el código, podemos hacer una convención de que la presencia de mok confirma la posibilidad de un guión, y su ausencia prohíbe la existencia de dicho guión. Si prohibimos que las clases de clientes escriban expectativas falsas, entonces en la búsqueda de NullOrderProvider resulta que no hay una simulación adecuada, porque nulo nunca regresa y, por lo tanto, no tiene sentido probar este escenario.
Si mok es una especificación, entonces mok es un reflejo de nuestros planes para un componente, y pueden y deben escribirse antes de la implementación.
Si un mok es una especificación, entonces es jerárquico y refleja el comportamiento del componente según ciertas condiciones.
Por lo tanto, nuestro moki puede multiplicarse demasiado: el moki incondicional siempre devuelve lo mismo, así como el moki con condiciones internas, como forIdOne_returningOne_forIdTwo_ReturningTwo_OrderProvider. La última enumeración y solo necesita crear un FakeOrderProvider con el comportamiento apropiado, que debe sincronizarse con la implementación real.
En consecuencia, si un mok es una especificación de un determinado comportamiento, la implementación de un componente no es más que sincronizar el comportamiento de un componente con sus moks.
Y este, quizás, es el argumento principal contra las turbas: la necesidad de sincronizar su comportamiento con la implementación real actual, hablaremos de esto por separado la próxima vez.