2010-02-26 6 views
6

Sto iniziando su un nuovo progetto (beh, riavvio di uno esistente) e sto tentando di adottare TDD (per l'ennesima volta) per tutti i vantaggi che dovrebbe comportare.TDD'ing MVC Controller per la progettazione dell'azionamento

Credo che il TDD comporterà i miei test guidandomi a scrivere solo il codice che ho bisogno di scrivere, ma mi guiderà a scrivere il codice che HO BISOGNO e non ne lascerò fuori.

Questo è dove il mio attuale stato di incertezza viene in

consideri la storia:.

"un utente deve essere in grado di aggiungere un widget, facendo in modo che siano adottate per visualizzare i dettagli della nuova aggiunto widget. "

OK, quindi funziona dall'interfaccia utente (poiché è lì che un utente aggiungerà il proprio widget e non utilizzando Visual Studio e un insieme di assiemi che scrivo) ... Inizio con il seguente test, scrivendo il minimo in modo che il test passi.

Così ho iniziato con il controller lanciare NotImplementedException, quindi restituire una vista() ... il seguente è stato il primo punto in cui avevo scritto il minor numero di linee che potevo fare per passare il test.

[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"); 
    } 
} 

Ora mi rendo conto che ulteriori test per i modelli validi e la verifica dello stato di modello si evolveranno da storie aggiuntive.

Tuttavia, non riesco a vedere un percorso chiaro su come sfruttare i test aggiuntivi per guidare ulteriore codice all'interno del controller.

Ad esempio, so che a un certo punto desidero effettuare una chiamata WidgetService dall'azione Create. Mi manca qualcosa di ovvio (non potendo vedere il legno per gli alberi come materiale) come posso progredire il codice del controller con test aggiuntivi?

Parlando di WidgetService, mi aspetto che scriverò un WidgetServiceTester e per il momento i riferimenti all'interno del controller saranno probabilmente presi in giro.

Alcuni pensieri che ho avuto ...

  • Creare un nuovo test chiamato Create_IfModelIsValid_WidgetIsAddedToRepository, ma come fa questo chiaramente conduce su chiamate di servizio nella azione di controllo?
  • Ho bisogno di scrivere una storia più dettagliata affermando che il modello deve essere inserito nel repository/database, ecc.?
  • Sto confondendo elementi di TDD e XP?

Grazie per la lettura, gradirei qualsiasi feedback e approfondimenti sulle migliori pratiche per il progresso.

Joe.

EDIT 27 Feb 2010

ho trovato il seguente articolo Iterazione # 6 - Usare Test-Driven Development (su asp.net) (http://www.asp.net/%28S%28ywiyuluxr3qb2dfva1z5lgeg%29%29/learn/mvc/tutorial-31-cs.aspx) che dimostra il genere di cosa che cercavo, ma che sembrano considerare l'aggiunta di repository/servizio al controller come ri-factoring ... Personalmente non sono d'accordo, mi sbaglio?:)

Ho intenzione di pensare a scrivere un test che controlla i ViewData dell'azione Dettagli e aggiornare questa domanda una volta che ho.

risposta

1

Joe. Sento molto la stessa incertezza, a volte. Ma penso anche che la maggior parte di ciò che ti manca sono quelle storie originali. In primo luogo, dovresti scomporre la tua storia solo un po 'per creare i requisiti effettivi degli sviluppatori. Ecco cosa intendo:

"Un utente deve essere in grado di aggiungere un widget, facendo in modo che vengano presi per visualizzare i dettagli del widget appena aggiunto."

Ok, quindi per me, che si scatena domande come segue, che possono aiutare a pensare di test:

"Un utente" - dove ha fatto l'utente viene? Come sono registrati? (se stai usando l'AccountController e i test predefiniti, questo è già presente, altrimenti dovrai test per ottenere il modulo di accesso, accesso, convalida degli accessi riusciti e non riusciti, ecc.)

"aggiungi un widget "- a cosa (non intendo implementazione, sto solo sottolineando che questo implica che stai andando a colpire un repository o un servizio, a meno che" add "significhi semplicemente aggiungerlo all'istanza in esecuzione e non hai bisogno di persistenza)? il widget deve essere valido a questo punto, oppure è possibile salvare e rendere validi i widdgets in seguito? Ciò implica che un repository o un servizio abbiano un metodo hit (save(), insert(), add(), qualunque sia (non i componenti interni del metodo, finché non si arriva a testare il servizio/repo, solo che il controller fa il suo lavoro chiamandolo), controlla cosa succede su un widget valido/non valido (devi espandere la tua storia un po 'o aggiungere una storia per coprire ciò che dovrebbe accadere su widget validi/non validi)

"facendo così sono preso per visualizzare i dettagli del widget appena aggiunto "- riformulato leggermente, ma sostanzialmente quello che hai detto. Sempre? o solo in caso di successo? Questa vista è modificabile o di sola lettura (ad esempio Modifica azione o Dettagli azione?) C'è un messaggio per il utente che dice loro che hanno avuto successo, o dovrebbero dedurre dal fatto che stanno visualizzando il loro widget che hanno avuto successo? Questo dovrebbe guidare test che fanno cose come controllare le proprietà sul risultato di azione restituito e che ck i valori memorizzati in TempData (messaggio di stato) e controllano cosa succede in entrambi i percorsi (successo o fallimento).

questo è solo uno scatto veloce, ma in fondo è il processo del pensiero. puoi anche fare le stesse w/altre storie e, per questo, generare nuove storie per coprire un maggior numero di comportamenti applicativi.

Un paio di pensieri sul progetto in corso.

Il tuo prossimo test dovrebbe guardare prima quello che ho scritto sopra, che è il fatto che il controllore crea un'azione POST dovrebbe 1) ricevere i dati necessari (i tuoi parametri), 2) chiamare quel servizio/repository per "aggiungere" il widget , 3) possibilmente fare qualcosa se quell'aggiunta non va a buon fine (questo è nel tuo progetto, sono arrivato dove i miei controller presumono che tutto andrà bene e gestirò i fallimenti attraverso gli attributi, ma questa è una decisione di design personale), 4) reindirizzare a dettagli.

Quindi, il tuo prossimo test userebbe un simulato (preferisco la libreria moq su google, ma qualunque cosa tu abbia funzionerà). Avrai bisogno di un'interfaccia per descrivere il servizio che il tuo controller chiamerà e passerai a un'implementazione fittizia di quella sul tuo controller in prova per assicurarti che chiami il metodo corretto.In Moq, che sarebbe simile a questa:

[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)); 
} 

che rende alcune ipotesi di progetto, naturalmente, ma la tensione di come scrivere i test vs come gli oggetti dovrebbero essere chiamati/gestire le cose fa parte di ciò che rende TDD così prezioso.