2010-12-08 13 views
5

Necesito desarrollar un algoritmo bastante simple, pero me siento confundido sobre cómo escribir mejor una prueba para ello.¿Qué debo hacer para probar esta unidad?

Descripción general: el usuario debe poder eliminar un plan. Plan tiene Tareas asociadas, también deben eliminarse (siempre que no estén hechas).

Pseudo-código como la forma en que el algoritmo debe comportarse:

PlanController.DeletePlan(plan) 
    => 
    PlanDbRepository.DeletePlan() 
     ForEach Task t in plan.Tasks 
      If t.Status = Status.Open Then 
      TaskDbRepository.DeleteTask(t) 
      End If 
     End ForEach 

Ahora por lo que tengo entendido, no se supone que las pruebas unitarias para tocar la base de datos o generalmente requieren el acceso a cualquiera de los sistemas externos, por lo que' m adivinar que tengo dos opciones:

1) Mock a cabo las llamadas del repositorio, y comprobar si han sido llamados el número apropiado de veces como afirma

2) Cree resguardos para ambas clases de repositorio, establezca su indicador de eliminación manualmente y luego verifique que los objetos apropiados hayan sido marcados para su eliminación.

En ambos enfoques, la gran pregunta es: ¿Qué estoy exactamente probando aquí? ¿Cuál es el valor EXTRA que tales pruebas me darían?

Cualquier idea en esto sería muy apreciada. Esto técnicamente no está vinculado a ningún marco específico de pruebas de unidades, aunque tenemos RhinoMocks para ser utilizado. Pero preferiría una explicación general, para poder entender esto.

Respuesta

4

Debería burlarse del repositorio y luego construir un plan ficticio en su unidad de prueba que contenga tareas abiertas y cerradas. A continuación, llame al método real que pasa este plan y, al final, verifique que se haya llamado al método DeleteTask con los argumentos correctos (tareas con solo estado = Abrir). De esta forma, se aseguraría de que el método elimine solo las tareas abiertas asociadas a este plan. Tampoco olvide (probablemente en una prueba de unidad separada) verificar que el plan en sí se ha eliminado al afirmar que se ha llamado al método DeletePlan en el objeto que está pasando.

2

Para agregar a la respuesta de Darin me gustaría decirte lo que realmente estás probando. Hay un poco de lógica de negocios allí, por ejemplo, el control del estado.

Esta prueba de unidad puede parecer un poco tonta en este momento, pero ¿qué pasa con los cambios futuros en su código y modelo? Esta prueba es necesaria para asegurarse de que esta funcionalidad aparentemente simple siempre funcione.

2

Como ha observado, está probando que la lógica en el algoritmo se comporta como se esperaba. Su enfoque es correcto, pero tenga en cuenta el futuro. Meses después, este algoritmo puede necesitar ser cambiado, un desarrollador diferente lo interrumpe y lo rehace, perdiendo una pieza crítica de la lógica. Sus pruebas unitarias ahora fallarán, y el desarrollador será alertado de su error. Las pruebas unitarias son útiles al inicio, y semanas/meses/años en el futuro también.

Si quiere agregar más, considere cómo se maneja la falla. Haga que su DB haga una excepción en el comando de eliminación, pruebe que su algoritmo maneja esto correctamente.

2

El valor extra proporcionado por las pruebas es verificar que el código haga lo correcto (en este caso, elimine el plan, elimine las tareas abiertas asociadas con el plan y deje las tareas cerradas asociadas con el plan).

Suponiendo que haya realizado pruebas para sus clases de repositorio (es decir, que hagan las cosas correctas cuando se les solicite eliminar), entonces todo lo que necesita hacer es verificar que los métodos de eliminación se llamen apropiadamente.

Algunas pruebas que podría escribir son:
¿Eliminar un plan vacío solo llama al DeletePlan?
¿Eliminar un plan con dos tareas abiertas llama al DeleteTask para ambas tareas?
¿Eliminar un plan con dos tareas cerradas no llama al DeleteTask en absoluto?
¿Eliminar una planilla con una tarea abierta y una cerrada llama al DeleteTask una vez en la tarea correcta?

Editar: Sin embargo, usaría la respuesta de Darin como la forma de hacerlo.

0

No escribiría pruebas unitarias para esto porque para mí esto no es un comportamiento de prueba sino una implementación. Si en algún momento desea arriesgarse a que el comportamiento no elimine las tareas, sino que las ajuste a un estado de "deshabilitado" o "ignorado", las pruebas de su unidad fallarán. Si prueba todos los controladores de esta manera, las pruebas de su unidad son muy frágiles y deberán cambiarse con frecuencia.

Refactorice la lógica de negocio a una 'TaskRemovalStrategy' si desea probar la lógica de negocio para esto y dejar los detalles de implementación de la eliminación a la clase misma.

0

IMO puede escribir sus pruebas unitarias en torno al resumen PlanRepository y las mismas pruebas también deberían ser útiles para probar la integridad de datos en la base de datos.

Por ejemplo, podría escribir una prueba -

void DeletePlanTest() 
{ 
    PlanRepository repo = new PlanDbRepository("connection string"); 
    repo.CreateNewPlan(); // create plan and populate with tasks 
    AssertIsTrue(repo.Plan.OpenTasks.Count == 2); // check tasks are in open state 
    repo.DeletePlan(); 
    AssertIsTrue(repo.Plan.OpenTasks.Count == 0); 
} 

Esta prueba funcionará incluso si su repositorio elimina el plan y su base de datos elimina las tareas relacionadas a través de un disparador en cascada eliminar.

El valor de dicha prueba es si la prueba se ejecuta para PlanDbRepository o MockRepository, pero seguirá verificando que el comportamiento sea correcto. Entonces, cuando cambia cualquier código de repositorio o incluso su esquema de base de datos, puede ejecutar las pruebas para comprobar que no haya nada roto.

Puede crear tales pruebas que cubran todos los comportamientos posibles de su repositorio y luego las use para asegurarse de que ninguno de sus cambios interrumpa la implementación.

También puede parametrizar esta prueba con una instancia de repositorio concreto y reutilizarla para probar futuras implementaciones de repositorios.

1

Interesante, encuentro que las pruebas unitarias ayudan a enfocar la mente en las especificaciones. Para ello vamos me haga esta pregunta ...

Si tengo un plan con 3 tareas:

Plan1 { 
Task1: completed 
Task2: todo 
Task3: todo 
} 

y llamo borrar en ellos, lo que debería ocurrir la que el Plan?

Plan1 : ? 
Task1: not deleted 
Task2: deleted 
Task3: deleted 

¿Se ha eliminado plan1, orphaning task1? o ¿está marcado de otra forma?

Esta es una gran parte del valor que veo en pruebas de unidad (Aunque es sólo 1 de los 4 valores: 1) Spec 2) Comentarios 3) Regresión 4) granularidad

cuanto a cómo probar, no sugeriría burlas en absoluto. Yo consideraría un método de 2 partes La primera se vería como

public void DeletePlan(Plan p) 
{ 
    var objectsToDelete = GetDeletedPlanObjects(p); 
    DeleteObjects(objectsToDelete); 
} 

Y no me probar este método. Probaría el método GetDeletedPlanObjects, que de todos modos no tocaría la base de datos, y le permitiría enviar escenarios como la situación anterior ... que luego afirmaría con www.approvaltests.com, pero esa es otra historia: -)

Happy Testing, Llewellyn

Cuestiones relacionadas