Контроллер модульного тестирования ASP.NET MVC с HttpContext
Я пытаюсь написать модульный тест для моего одного контроллера, чтобы убедиться, что представление было возвращено правильно, но у этого контроллера есть базовый контроллер, который обращается к HttpContext.Current.Session. Каждый раз, когда я создаю новый экземпляр моего контроллера, он вызывает конструктор basecontroller, и тест завершается неудачей с исключением нулевого указателя в HttpContext.Current.Session. Вот код:
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);
}
Любые идеи о том, как смоделировать (используя Moq) сеанс, к которому осуществляется доступ в базовом контроллере, чтобы выполнить тест в контроллере-потомке?
5 ответов
Если вы используете Typemock, вы можете сделать это:
Isolate.WhenCalled(()=>controller.HttpContext.Current.Session["UserID"])
.WillReturn("your id");
Тестовый код будет выглядеть так:
[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);
}
Если вы не используете Typemock или Moles, вы не можете.
В ASP.NET MVC вы не должны использовать HttpContext.Current. Измените свой базовый класс на использование ControllerBase.ControllerContext - он имеет свойство HttpContext, которое предоставляет тестируемый класс HttpContextBase.
Вот пример того, как вы можете использовать Moq для настройки Mock HttpContextBase:
var httpCtxStub = new Mock<HttpContextBase>();
var controllerCtx = new ControllerContext();
controllerCtx.HttpContext = httpCtxStub.Object;
sut.ControllerContext = controllerCtx;
// Exercise and verify the sut
где sut
представляет тестируемую систему (SUT), то есть контроллер, который вы хотите протестировать.
Вы, вероятно, должны использовать ActionFilter вместо базового класса для такого рода вещей
[UserIdBind]
public class IndexController : Controller
{
public ActionResult Index()
{
return View("Index.aspx");
}
}
Snippet:
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;
Реализация 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
}
}
Я бы ознакомился с перечисленной здесь книгой по ASP.NET-MVC - в конце есть хороший раздел о Mocking framewors - http://www.hanselman.com/blog/FreeASPNETMVCEBookNerdDinnercomWalkthrough.aspx