2010-02-26 14 views
6

Estoy comenzando un nuevo proyecto (bueno, reiniciando uno existente) y tratando de adoptar TDD (por enésima vez) para todos los beneficios que debería traer.TDD'ing Controladores MVC para controlar el diseño

Creo que TDD hará que mis pruebas me obliguen a escribir solo el código que necesito escribir, pero me llevará a escribir el código que NECESITO y no a dejarlo.

Aquí es donde mi actual estado de incertidumbre viene en

cuenta la historia:.

"Un usuario debe ser capaz de añadir un widget, haciendo así que se toman para ver los detalles de la nueva widget agregado ".

OK, así que trabajando desde la interfaz de usuario (ya que es allí donde un usuario agregará su widget, y no usando Visual Studio y un conjunto de ensambles que escribo) ... comienzo con la siguiente prueba, escribiendo el mínimo para que la prueba pase.

Así que comencé con el controlador lanzando una NotImplementedException, y luego devolviendo una vista() ... el siguiente fue el primer punto que escribí la menor cantidad de líneas que pude para pasar la prueba.

[TestFixture] 
public class WidgetControllerTester 
{ 

    [Test] 
    public void Create_IfBusinessModelIsValid_ReturnRedirectToRouteResultToDetailsAction() 
    { 
    // Arrange 
     var currentUser = new User 
           { 
            DisplayName = "Fred", 
            Email = "[email protected]", 
            Password = "pass", 
            Status = UserStatus.Active 
           }; 
     var model = new WidgetModel(); 
     var controller = new WidgetController(); 
     // Act 
     var actionResult = controller.Create(currentUser, model); 
     // Assert 
     actionResult.AssertActionRedirect().ToAction("Details"); 
    } 
} 

public class WidgetModel 
{ 
} 

public class WidgetController: Controller 
{ 
    public ActionResult Create() 
    { 
     return View("Create"); 
    } 

    [HttpPost] 
    public ActionResult Create(User currentUser, Widget model) 
    { 
     return RedirectToAction("Details"); 
    } 
} 

Ahora me doy cuenta de que las pruebas adicionales para los modelos no válidos y la comprobación del estado del modelo evolucionarán a partir de historias adicionales.

Sin embargo, no veo una forma clara de cómo aprovechar las pruebas adicionales para obtener más código dentro del controlador.

Por ejemplo, sé que en algún momento desearé hacer una llamada a WidgetService desde la acción Crear. ¿Me estoy perdiendo algo obvio (no ser capaz de ver la madera para los árboles?) ¿Cómo puedo progresar el código del controlador con pruebas adicionales?

Hablando del WidgetService, espero que escriba un WidgetServiceTester y por el momento será muy probable que se burlen las referencias dentro del controlador.

Algunos pensamientos que he tenido ...

  • Crear una nueva prueba llamada Create_IfModelIsValid_WidgetIsAddedToRepository, pero ¿cómo afecta esto conduce claramente a las llamadas de servicio en la acción del controlador?
  • Tengo que escribir una historia más detallada que indique que el modelo debe insertarse en el repositorio/base de datos, etc.
  • ¿Estoy confundiendo elementos de TDD y XP?

Gracias por leer, agradecería cualquier comentario e ideas sobre las mejores prácticas para progresar.

Joe.

EDITAR 27 de Feb 2010

me encontré con el siguiente artículo iteración # 6 - Uso Test-Driven Development (en asp.net) (http://www.asp.net/%28S%28ywiyuluxr3qb2dfva1z5lgeg%29%29/learn/mvc/tutorial-31-cs.aspx) que demuestra el tipo de cosa que fue después, sin embargo, parecen considerar la adición de repositorio/servicio al controlador como factorización ... personalmente no estoy de acuerdo, ¿me equivoco?:)

Voy a pensar en escribir una prueba que verifique la acción ViewData of the Details y actualice esta pregunta una vez que lo haya hecho.

Respuesta

1

Joe. A veces siento la misma incertidumbre. Pero también creo que la mayoría de lo que te estás perdiendo son esas historias iniciales. En primer lugar, debe descomponer su historia un poco para crear los requisitos reales del desarrollador. Esto es lo que quiero decir:

"Un usuario debe poder agregar un widget, de modo que se toman para ver los detalles del widget recién agregado."

Ok, por lo que a mí, que estalla preguntas como la siguiente, que puede ayudar a pensar de pruebas:

"Un usuario" - ¿de dónde el usuario viene? ¿Cómo están conectados? (Si está usando el AccountController y las pruebas predeterminadas, entonces esto ya está allí; si no, querrá pruebas para obtener el formulario de inicio de sesión, iniciar sesión, validar inicios de sesión exitosos y fallidos, etc.)

"add un widget "- a qué (no me refiero a la implementación, solo señalo que esto implica que vas a acceder a un repositorio o a un servicio, a menos que 'agregar' solo signifique agregarlo a la instancia en ejecución y no necesitas persistencia)? ¿el widget debe ser válido en este punto, o pueden guardarse widdgets inválidos y hacerse válidos más tarde? Esto implica para mí que un repositorio o servicio tenga un método golpeado (save(), insert(), add(), lo que sea (no las partes internas del método, hasta que llegue a probar su servicio/repositorio, solo que el controlador hace su trabajo llamándolo), verifique lo que ocurre en un widget válido/inválido (necesita expandir un poco su historia o agregar una historia para cubrir lo que debería suceder en los widgets válidos/inválidos)

"haciéndolo así tomado para ver los detalles del widget recientemente agregado "- reescrito levemente, pero básicamente lo que dijo. Siempre? o solo en caso de éxito? ¿Es esta vista editable o de solo lectura (es decir, acción de edición o acción Detalles)? ¿Hay algún mensaje para el usuario que les dice que han tenido éxito, o ¿deberían inferir del hecho de que están viendo su widget que tuvieron éxito? Esto debería impulsar pruebas que hagan cosas como verificar propiedades en el resultado de la acción devuelto y che Acceda a los valores almacenados en TempData (mensaje de estado) y verifique lo que sucede en ambas rutas (éxito o falla).

eso es solo una instantánea, pero básicamente ese es el proceso de reflexión. también puede hacer lo mismo con otras historias, y para ese asunto generar nuevas historias para cubrir más comportamiento de la aplicación.

Un par de ideas sobre su diseño en el futuro.

Su próxima prueba debe mirar lo que escribí arriba, que es el hecho de que la acción POST del controlador debe 1) recibir los datos necesarios (sus parámetros), 2) llamar a ese servicio/repositorio para "agregar" el widget , 3) posiblemente haga algo si ese complemento falla (esto está en su diseño; he llegado a donde mis controladores suponen que todo irá bien y manejo fallas mediante atributos, pero esa es una decisión de diseño personal), 4) redirigir a detalles.

Por lo tanto, su próxima prueba sería usar un simulacro (prefiero la biblioteca moq en el código de Google, pero lo que tenga funcionará). Necesitará alguna interfaz para describir el servicio que llamará su controlador, y pasará una implementación simulada de eso a su controlador bajo prueba para asegurarse de que está llamando al método correcto.En Moq, eso sería algo como esto:

[Test] 
public void Create_CallsRepository() 
{ 
    // Arrange 
    var currentUser = new User 
          { 
           DisplayName = "Fred", 
           Email = "[email protected]", 
           Password = "pass", 
           Status = UserStatus.Active 
          }; 
    var model = new WidgetModel(); 
    var mockService = new Mock<IService<WidgetModel>(); 
    mockService.Setup(s=>s.Add(model)); //.Returns(whatever) if it returns something 
    var controller = new WidgetController(mockService.Object); 

    // Act 
    var actionResult = controller.Create(currentUser, model); 

    // Assert 
    mockService.Verify(s=>s.Add(model)); 
} 

que hace que algunas hipótesis de diseño, por supuesto, pero la tensión de la forma de escribir sus pruebas vs cómo sus objetos deben ser llamados/manejar las cosas es parte de lo hace que TDD sea tan valioso

Cuestiones relacionadas