visual unit test studio make how ejemplo unit-testing testing mocking tdd

unit testing - unit - ¿Cómo probar un método en una unidad cuyo efecto secundario es llamar a otro método?



unit testing c# 2017 (3)

Creo que Paul tiene razón: poner los cambios de estado basados ​​en el estado entrante en una máquina de estado, es decir, un objeto cuya responsabilidad es determinar qué viene después. Esto puede sonar tonto, porque mueve el mismo código a otro objeto, pero al menos esto pone al controlador en una dieta. No debe preocuparse por demasiados detalles para mantenerse.

Me preocupa updateViewState , sin embargo. ¿Por qué toma el mismo tipo de parámetro que la devolución de llamada del controlador para la interacción del usuario? ¿Puedes modelar esto de manera diferente? Es difícil decirle algo específico sin mirar el flujo de información (un diagrama de secuencia detallado con comentarios podría ayudar), porque generalmente la percepción real de problemas como estos yace en múltiples niveles más profundos en la pila de llamadas. Sin conocimiento sobre el significado de todo esto, es difícil encontrar una solución enlatada que encaje.

Preguntas que pueden ayudar:

  • si el State representa 3 (?) interacciones del usuario que pasan por el mismo túnel, ¿puede modelar las acciones para tomar como estrategia o comando?
  • Si está doneWithCurrentState representa terminar uno de los muchos modos de interacción, ¿realmente necesita usar un método doneWithCurrentState compartido? ¿No podrías usar tres devoluciones de llamada diferentes? Tal vez este es el tipo equivocado de abstracción. ("Do not Repeat Yourself" no se trata de código sino de cosas que cambian (en) de manera dependiente )

Aquí está mi ejemplo:

void doneWithCurrentState(State state) { switch (state) { case State.Normal: // this method is never actually called with State.Normal break; case State.Editing: controller.updateViewState(State.Normal); database.updateObjectWithDetails(controller.getObjectDetailsFromViews()) break; case State.Focus: controller.updateViewState(State.Editing); break; } }

Mi controller llama al doneWithCurrentState cuando se presiona un botón específico. Los states son posiciones diferentes en la pantalla que las vistas de ese controller pueden asumir.

Si el estado actual es Normal , el botón estará oculto.

Si se presiona el botón con el estado actual como Editing , se doneWithCurrentState método doneWithCurrentState (digo el método porque está realmente dentro de una clase ``) y debería cambiar el estado de las vistas del controlador a Normal y actualizar el Object en la database usando ObjectDetails (que es solo una estructura con datos que se utilizarán para actualizar el Object ) que se debe recuperar de las vistas del controlador (es decir, campos de texto, casillas de verificación, etc.).

Si se presiona el botón con el estado actual como Focus , simplemente debería volver al estado de Editing .

Estoy probando la unidad de esta manera:

void testDoneWithCurrentStateEditing() { mockController.objectDetails = ...; myClass.doneWithCurrentState(State.Editing); AssertEqual(mockController.viewState, State.Normal, "controller state should change to Normal"); AssertTrue(mockDatabase.updateObjectWithDetailsWasCalled, "updateObjectWithDetails should be called"); AssertEqual(mockDatabase.updatedWithObjectDetail, mockController.objectDetails, "database should be updated with corresponding objectDetails"); } void testDoneWithCurrentStateFocus() { myClass.doneWithCurrentState(State.Focus); AssertEqual(mockController.viewState, State.Editing, "controller state should change to Editing"); AssertFalse(mockDatabase.updateObjectWithDetailsWasCalled, "updateObjectWithDetails should not be called"); }

Pero parece estar equivocado, parece que estoy afirmando que se realizó una llamada a un método y luego estoy haciendo la llamada ... es como afirmar los métodos setter y getter.

¿Cuál sería la forma correcta de probar el método doneWithCurrentState ? Como parte de la respuesta, acepto algo así como "primero debes refactorizar el método para separar mejor estas preocupaciones ...".

Gracias.


En primer lugar, considere usar la máquina de estado para las transiciones de estado, saldrá de la empresa de derivación de declaración de cambio, lo que resultará en una gran simplificación de sus pruebas.

Luego, trate sus pruebas como una fuente potencial para el código y el diseño de olores. Si es difícil escribir una prueba para un fragmento de código, probablemente el código carece de calidad (ruptura de SRP, demasiado acoplado, etc.) y se puede simplificar / mejorar.

void doneWithCurrentState(State state) { State nextState = this.stateMachine.GetNextState(state); controller.updateViewState(nextState); if(nextState == State.Editing) database.updateObjectWithDetails(controller.getObjectDetailsFromViews()); }

Luego puede observar que puede sacar la llamada a la máquina de estado del método y pasar el siguiente Estado.

//whoever calls this method should get nextState from state machine. void doneWithCurrentState(State nextState) { controller.updateViewState(nextState); if(nextState == State.Editing) database.updateObjectWithDetails(controller.getObjectDetailsFromViews()); }

y así sucesivamente ... escribirá pruebas simples para las transiciones de estado en las pruebas de su máquina de estado ... ¿su complejidad general del código se reduce y todo es bondad? Bueno, apenas hay un límite para el nivel de bondad que puede lograr y puedo ver múltiples maneras en que el código puede ser limpiado aún más.

Según su pregunta original, cómo pasar el código de su clase hace una llamada a ''base de datos'' o ''controlador'' con los parámetros adecuados con estado específico. Se lo está haciendo "bien", lo que se supone que deben hacer los burladores . Sin embargo, hay mejores maneras. Considere el diseño basado en eventos. ¿Qué pasa si su controlador puede disparar los eventos como "NextState" y su objeto de "base de datos" puede suscribirse a él? Entonces, todas sus pruebas deben probarse para saber si se ha activado el evento apropiado y no incluir nada sobre la base de datos (eliminando las dependencias :))


Si no escribió esto primero, una forma obvia de escribirlo sería escribir un caso, luego copiar y pegar en el siguiente caso. Un error fácil de hacer en ese caso sería olvidar actualizar el parámetro a updateViewState (). Entonces (por ejemplo) puede que te encuentres yendo de State.Focus a State.Normal. La prueba que ha escrito, aunque le parezca débil, la protege de errores de esa naturaleza. Entonces creo que está haciendo lo que debería.