2009-08-04 16 views
21

Ho un apparecchio di prova unità in cui sto provando a testare ControllerAction su un controller ASP.NET MVC che viene utilizzato per le funzioni di appartenenza su un'app Web. Sto cercando di deridere HttpContext per i test. Il ControllerAction in prova in realtà imposta le proprietà sul HttpContext, come i valori di sessione, valori Response.Cookies, ecc Questo non è tutto il codice, ma qui è un esempio di massima del test che sto cercando di arrivare a correre :Mocking HttpContextBase con Moq

[Test] 
public void ValidRegistrationDataSuccessfullyCreatesAndRegistersUser() 
{ 
    var context = new Mock<HttpContextBase>() {DefaultValue = DefaultValue.Mock}; 
    context.SetupAllProperties(); 
    var provider = new Mock<MembershipProvider>(new object[] {context.Object}); 
    var controller = new AccountController(context.Object, provider.Object); 
    // This just sets up a local FormCollection object with valid user data 
    // in it to use to attempt the registration 
    InitializeValidFormData(); 
    ActionResult result = controller.Register(_registrationData); 
    Assert.IsInstanceOfType(typeof(ViewResult), result); 
    // Here is where I'd like to attempt to do Assertions against properties 
    // of the HttpContext, like ensuring that a Session object called "User" 
    // exists, and new auth cookie exists on the Response.Cookies collection. 
    // So far I've been unable to successfully check the values of those properties. 
    // I've been unsuccessful in getting those properties setup correctly on my 
    // mock object so that my ControllerAction can actually *set* their values, 
    // and that I can make assertions on them afterwards. The above code actually 
    // generates a StackOverflowException (which I've reported) on the 
    // context.SetupAllProperties() call. What am I doing wrong, or what do I need 
    // to do to be able to set and assert on those context properties? 
} 

Non sei sicuro di quello che sto facendo male, ma mi piacerebbe se qualcuno mi potrebbe punto nella giusta direzione e mi ha spiegato come preparare questo oggetto HttpContextBase finta tale che il mio controller può effettivamente impostare i valori sulle sue proprietà, e posso fare asserzioni su quelle proprietà per assicurarmi che la mia ControllerAction stia facendo quello che mi serve.

Mi sto avvicinando a questo nel modo sbagliato? So che Controller MVC hanno un ControllerContext che posso usare per impostare i valori per la sessione, ecc, ma non riesco a capire come una cosa del genere potesse essere preso in giro senza iniettare esso. C'è un modo per farlo, invece? (Devo anche essere in grado di passare il contesto al mio MembershipProvider) Sarebbe un approccio migliore?

Grazie.

risposta

31

Sto usando una versione di un certo codice Steve Sanderson incluso nel suo Pro Asp.NET MVC book ... e attualmente sto avendo un dilemma morale se è corretto pubblicare il codice qui. Che ne dici di scendere a compromessi con una versione molto ridotta? ;)

Quindi questo può essere facilmente riutilizzato, creare una classe simile a quella qui sotto che si passa il controller. In questo modo impostare le deride e metterli a del controller ControllerContext

public class ContextMocks 
{ 
    public Moq.Mock<HttpContextBase> HttpContext { get; set; } 
    public Moq.Mock<HttpRequestBase> Request { get; set; } 
    public RouteData RouteData { get; set; } 

    public ContextMocks(Controller controller) 
    { 
     //define context objects 
     HttpContext = new Moq.Mock<HttpContextBase>(); 
     HttpContext.Setup(x => x.Request).Returns(Request.Object); 
     //you would setup Response, Session, etc similarly with either mocks or fakes 

     //apply context to controller 
     RequestContext rc = new RequestContext(HttpContext.Object, new RouteData()); 
     controller.ControllerContext = new ControllerContext(rc, controller); 
    } 
} 

E poi nel vostro metodo di test che si era appena creare un'istanza di ContextMocks e passare l'oggetto di controllo si sta testando:

[Test] 
Public void test() 
{ 
    var mocks = new ContextMocks(controller); 
    var req = controller.Request; 
    //do some asserts on Request object 
} 

sembra molto simile agli esempi di Craig, ma questo è con Moq v3. Devo dare puntelli a Steve Sanderson per questo - sto usando questo come base per testare tutti i tipi di altrimenti tradizionalmente difficili da test di roba: biscotti, sessione, metodo di richiesta, querystring e molto altro ancora!

+0

Ho il suo libro, ma l'ho preso solo un paio di giorni fa, quindi credo di non aver ancora ottenuto quella parte. Se tu potessi indicarmi la sezione di quel libro da cui hai preso il codice di esempio, sarebbe fantastico. Grazie mille. –

+0

Controlla la fine di ch.9. Non ce l'ho davanti, ma sembra che stia dando un'occhiata al sommario del libro su Amazon. –

+0

Trovato. Era esattamente * quello di cui avevo bisogno. Ha funzionato tutto ora. Grazie mille. –

22

Here's how I do it.

public static HttpContextBase FakeHttpContext() 
    { 
     var context = new Mock<HttpContextBase>(); 
     var request = new Mock<HttpRequestBase>(); 
     var response = new Mock<HttpResponseBase>(); 
     var session = new Mock<HttpSessionStateBase>(); 
     var server = new Mock<HttpServerUtilityBase>(); 
     var user = new Mock<IPrincipal>(); 
     var identity = new Mock<IIdentity>(); 

     request.Expect(req => req.ApplicationPath).Returns("~/"); 
     request.Expect(req => req.AppRelativeCurrentExecutionFilePath).Returns("~/"); 
     request.Expect(req => req.PathInfo).Returns(string.Empty); 
     response.Expect(res => res.ApplyAppPathModifier(It.IsAny<string>())) 
      .Returns((string virtualPath) => virtualPath); 
     user.Expect(usr => usr.Identity).Returns(identity.Object); 
     identity.ExpectGet(ident => ident.IsAuthenticated).Returns(true); 

     context.Expect(ctx => ctx.Request).Returns(request.Object); 
     context.Expect(ctx => ctx.Response).Returns(response.Object); 
     context.Expect(ctx => ctx.Session).Returns(session.Object); 
     context.Expect(ctx => ctx.Server).Returns(server.Object); 
     context.Expect(ctx => ctx.User).Returns(user.Object); 

     return context.Object; 
    } 

Questa è una versione migliorata del the MvcMockHelpers library released by Scott Hanselman. Questo è il codice Moq 2.0; la sintassi è leggermente diversa in 3.

+0

Sì, grazie, ma sto usando Moq 3.1.416.3, quindi sarebbe davvero utile vedere la sintassi per quella versione. Principalmente perché, apparentemente, le proprietà di "impostazione" sono piuttosto diverse in 3.x se ho capito bene. Grazie per l'esempio però. –

+6

Sostituisci l'attesa con l'installazione e dovrebbe funzionare così com'è. –

+0

'request.Expect (req => req.ApplicationPath) .Returns (" ~/");' dovrebbe essere 'request.Expect (req => req.ApplicationPath) .Returns ("/");' –