2012-03-21 14 views
8

Sono abbastanza nuovo per i test delle unità e concetti di simulazione. Sto cercando di capire come scrivere un buon banco di prova per la base out-of-the codice di registrazione utente che vedi qui sotto:ASP .Net MVC 3: Unità di controllo delle azioni del controllore

[HttpPost] 
public ActionResult Register(RegisterModel model) 
{ 
    if (ModelState.IsValid) 
    { 
     // Attempt to register the user 
     MembershipCreateStatus createStatus; 
     Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus); 

     if (createStatus == MembershipCreateStatus.Success) 
     { 
      FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */); 
      return RedirectToAction("Index", "Home"); 
     } 
     else 
     { 
      ModelState.AddModelError("", ErrorCodeToString(createStatus)); 
     } 
    } 

    // If we got this far, something failed, redisplay form 
    return View(model); 
} 

Qui di seguito sono alcuni dei punti specifici in cui ho bisogno la tua opinione/help:

  1. Non voglio necessariamente creare un nuovo utente nel database di appartenenza ASP .Net.
  2. In base al modello inoltrato, come faccio a verificare se l'utente è stato registrato correttamente o se ci sono stati errori nel processo.

risposta

25

Hai un problema con il tuo codice. L'azione dipende da un metodo statico: Membership.CreateUser. E come sapete i metodi statici sono PITA al test unitario.

Così si potrebbe indebolire l'accoppiamento con l'introduzione di un livello di astrazione:

public interface IMyService 
{ 
    MembershipCreateStatus CreateUser(string username, string password, string email); 
} 

e quindi avere qualche applicazione che avrebbe utilizzato l'attuale provider di appartenenze:

public class MyService: IMyService 
{ 
    public MembershipCreateStatus CreateUser(string username, string password, string email) 
    { 
     MembershipCreateStatus status; 
      Membership.CreateUser(username, password, email, null, null, true, null, out status); 
     return status; 
    } 
} 

e, infine, il controllore:

public class AccountController : Controller 
{ 
    private readonly IMyService _service; 
    public AccountController(IMyService service) 
    { 
     _service = service; 
    } 

    [HttpPost] 
    public ActionResult Register(RegisterModel model) 
    { 
     if (ModelState.IsValid) 
     { 
      // Attempt to register the user 
      var status = _service.CreateUser(model.UserName, model.Password, model.Email); 
      if (status == MembershipCreateStatus.Success) 
      { 
       FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */); 
       return RedirectToAction("Index", "Home"); 
      } 
      else 
      { 
       ModelState.AddModelError("", ErrorCodeToString(createStatus)); 
      } 
     } 

     // If we got this far, something failed, redisplay form 
     return View(model); 
    } 
} 

OK, ora che abbiamo indebolito l'accoppiamento potremmo noi e un quadro di derisione per deridere il servizio nel test unitario e renderlo banale.

Ad esempio, utilizzando Rhino Mocks è possibile creare i seguenti test per coprire i casi di guasto 2:

[TestMethod] 
public void Register_Action_Should_Redisplay_View_If_Model_Is_Invalid()  
{ 
    // arrange 
    var sut = new AccountController(null); 
    var model = new RegisterModel(); 
    sut.ModelState.AddModelError("", "invalid email"); 

    // act 
    var actual = sut.Register(model); 

    // assert 
    Assert.IsInstanceOfType(actual, typeof(ViewResult)); 
    var viewResult = actual as ViewResult; 
    Assert.AreEqual(model, viewResult.Model); 
} 

[TestMethod] 
public void Register_Action_Should_Redisplay_View_And_Add_Model_Error_If_Creation_Fails() 
{ 
    // arrange 
    var service = MockRepository.GenerateStub<IMyService>(); 
    service 
     .Stub(x => x.CreateUser(null, null, null)) 
     .IgnoreArguments() 
     .Return(MembershipCreateStatus.InvalidEmail); 
    var sut = new AccountController(service); 
    var model = new RegisterModel(); 

    // act 
    var actual = sut.Register(model); 

    // assert 
    Assert.IsInstanceOfType(actual, typeof(ViewResult)); 
    var viewResult = actual as ViewResult; 
    Assert.AreEqual(model, viewResult.Model); 
    Assert.IsFalse(sut.ModelState.IsValid); 
} 

La prova finale è il caso di successo. Abbiamo ancora un problema con esso. Il problema è la seguente riga:

FormsAuthentication.SetAuthCookie(model.UserName, false); 

Che cos'è questo? È una chiamata al metodo statico. Quindi procediamo come abbiamo fatto con il provider di appartenenza per indebolire l'accoppiamento tra il nostro controller e il sistema di autenticazione dei moduli.

+4

+1 per il grande e facile da seguire. – ljubomir

0

Per testare questo metodo si possono seguire due vie

  1. All'interno della classe di test creare una nuova classe che eredita da Membership classe ed eseguire l'override del metodo CreateUser.
  2. Utilizzare il Moq per deridere la classe.

Per il primo caso che controlla se username è uguale a "GoodUser" o "BADUSER" e generare un MembershipCreateStatus.Success o uno status diverso.

Per il secondo, imposterò due metodi che seguono la stessa idea dell'altro metodo. Vedi questo link per un esempio

Problemi correlati