2010-03-23 19 views
28

Estoy intentando escribir una prueba unitaria para mi controlador único para verificar si una vista se devolvió correctamente, pero este controlador tiene un controlador base que accede a HttpContext.Current.Session. Cada vez que creo una nueva instancia de mi controlador, se llama al constructor del controlador base y la prueba falla con una excepción de puntero nulo en HttpContext.Current.Session. Aquí está el código:Controlador de prueba de unidad ASP.NET MVC con HttpContext

public class BaseController : Controller 
{  
    protected BaseController() 
    { 
     ViewData["UserID"] = HttpContext.Current.Session["UserID"]; 
    } 
} 

public class IndexController : BaseController 
{ 
    public ActionResult Index() 
    { 
     return View("Index.aspx"); 
    } 
} 

    [TestMethod] 
    public void Retrieve_IndexTest() 
    { 
     // Arrange 
     const string expectedViewName = "Index"; 

     IndexController controller = new IndexController(); 

     // Act 
     var result = controller.Index() as ViewResult; 

     // Assert 
     Assert.IsNotNull(result, "Should have returned a ViewResult"); 
     Assert.AreEqual(expectedViewName, result.ViewName, "View name should have been {0}", expectedViewName); 
    } 

Cualquier ideas sobre cómo burlarse (usando Moq) la Sesión que se accede en el controlador de base de lo que la prueba en el controlador descendiente funcionará?

Respuesta

5

Si está utilizando Typemock, usted puede hacer esto: Código

Isolate.WhenCalled(()=>controller.HttpContext.Current.Session["UserID"]) 
.WillReturn("your id"); 

La prueba se verá así:

[TestMethod] 
public void Retrieve_IndexTest() 
{ 
    // Arrange 
    const string expectedViewName = "Index"; 

    IndexController controller = new IndexController(); 
    Isolate.WhenCalled(()=>controller.HttpContext.Current.Session["UserID"]) 
    .WillReturn("your id"); 
    // Act 
    var result = controller.Index() as ViewResult; 

    // Assert 
    Assert.IsNotNull(result, "Should have returned a ViewResult"); 
    Assert.AreEqual(expectedViewName, result.ViewName, "View name should have been {0}", expectedViewName); 
} 
3

Probablemente deberías usar un ActionFilter en lugar de una clase base para este tipo de cosas

[UserIdBind] 
public class IndexController : Controller 
{ 
    public ActionResult Index() 
    { 
     return View("Index.aspx"); 
    } 
} 
+0

Si utilizo el enfoque del filtro acción que tendría entonces para decorar cada acción con este atributo y con cerca de 400 acciones que ISN es factible – amurra

+0

@ user299592: No, no lo harías. Puede aplicarlo a nivel de clase (según el ejemplo) y lo aplicará a cada acción de esa clase. Si crees que va a ser más esfuerzo que burlarse de un contexto para cada prueba en cada acción (algunas o ninguna de las cuales usa realmente el campo que estás configurando en el constructor), es suficiente. – pdr

+0

Su correcto No lo vi en el nivel de clase, pero ¿no se llamaría todavía al filtro de acción al crear el objeto del controlador? – amurra

61

A menos que utilice Typemock o Moles, you can't.

En ASP.NET MVC no debe usar HttpContext.Current. Cambie su clase base para usar ControllerBase.ControllerContext - tiene una propiedad HttpContext que expone la clase HttpContextBase testable.

Aquí hay un ejemplo de cómo se puede utilizar Moq para establecer un HttpContextBase Mock:

var httpCtxStub = new Mock<HttpContextBase>(); 

var controllerCtx = new ControllerContext(); 
controllerCtx.HttpContext = httpCtxStub.Object; 

sut.ControllerContext = controllerCtx; 

// Exercise and verify the sut 

donde sut representa el sistema bajo prueba (SUT), es decir, el controlador que desea probar.

+0

¿me puede apuntar en la dirección de cómo burlarse de esto con Moles por favor? – BritishDeveloper

+0

Definitivamente el camino a seguir; Microsoft agregó HttpContextBase por esta razón precisa. Ahora si solo Cache y FormsAuthentication no estuvieran selladas/estáticas ... –

3

Fragmento:

var request = new SimpleWorkerRequest("/dummy", @"c:\inetpub\wwwroot\dummy", "dummy.html", null, new StringWriter()); 
var context = new HttpContext(request); 
SessionStateUtility.AddHttpSessionStateToContext(context, new TestSession()); 
HttpContext.Current = context; 

Implementación de TestSession():

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Web.SessionState; 

namespace m1k4.Framework.Test 
{ 
    public class TestSession : IHttpSessionState 
    { 
     private Dictionary<string, object> state = new Dictionary<string, object>(); 

     #region IHttpSessionState Members 

     public void Abandon() 
     { 
      throw new NotImplementedException(); 
     } 

     public void Add(string name, object value) 
     { 
      this.state.Add(name, value); 
     } 

     public void Clear() 
     { 
      throw new NotImplementedException(); 
     } 

     public int CodePage 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public System.Web.HttpCookieMode CookieMode 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public void CopyTo(Array array, int index) 
     { 
      throw new NotImplementedException(); 
     } 

     public int Count 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public System.Collections.IEnumerator GetEnumerator() 
     { 
      throw new NotImplementedException(); 
     } 

     public bool IsCookieless 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public bool IsNewSession 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public bool IsReadOnly 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public bool IsSynchronized 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public System.Collections.Specialized.NameObjectCollectionBase.KeysCollection Keys 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public int LCID 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public SessionStateMode Mode 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public void Remove(string name) 
     { 
      this.state.Remove(name); 
     } 

     public void RemoveAll() 
     { 
      this.state = new Dictionary<string, object>(); 
     } 

     public void RemoveAt(int index) 
     { 
      throw new NotImplementedException(); 
     } 

     public string SessionID 
     { 
      get 
      { 
       return "Test Session"; 
      } 
     } 

     public System.Web.HttpStaticObjectsCollection StaticObjects 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public object SyncRoot 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public int Timeout 
     { 
      get 
      { 
       return 10; 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public object this[int index] 
     { 
      get 
      { 
       throw new NotImplementedException(); 
      } 
      set 
      { 
       throw new NotImplementedException(); 
      } 
     } 

     public object this[string name] 
     { 
      get 
      { 
       return this.state[name]; 
      } 
      set 
      { 
       this.state[name] = value; 
      } 
     } 

     #endregion 
    } 
} 
+1

Si usa Rhino.Mocks, simplemente puede hacer esto 'SessionStateUtility.AddHttpSessionStateToContext (httpContext, MockRepository.GenerateStub ());' en lugar de implementar TestSession usted mismo. –

Cuestiones relacionadas