Я вижу много дискуссий вокруг HttpSessionState и asp.net MVC. Я пытаюсь написать тесты для приложения asp.net и задаюсь вопросом, можно ли издеваться над HttpSessionState, и если да, то как?
В настоящее время я использую Rhino Mocks и Nunit
Я вижу много дискуссий вокруг HttpSessionState и asp.net MVC. Я пытаюсь написать тесты для приложения asp.net и задаюсь вопросом, можно ли издеваться над HttpSessionState, и если да, то как?
В настоящее время я использую Rhino Mocks и Nunit
Гилберта,
Возможно, я слишком поздно для вас. Я использую MSpec, но я думаю, что понятия похожи. Мне нужно было издеваться над несколькими компонентами HttpContext в тестируемых контроллерах.
Я начал с этих следующих классов, чтобы макетировать необходимые (для моих целей) компоненты в HttpContextBase. Я перевернул только необходимые предметы внутри классов. Ваши потребности будут различаться в зависимости от макетов, которые вам нужны в контроллере. Это довольно легко добавить макеты по мере необходимости, как только вы понимаете шаблон.
public class MockHttpContext : HttpContextBase
{
private readonly HttpRequestBase _request = new MockHttpRequest();
private readonly HttpServerUtilityBase _server = new MockHttpServerUtilityBase();
private HttpSessionStateBase _session = new MockHttpSession();
public override HttpRequestBase Request
{
get { return _request; }
}
public override HttpServerUtilityBase Server
{
get { return _server; }
}
public override HttpSessionStateBase Session
{
get { return _session; }
}
}
public class MockHttpRequest : HttpRequestBase
{
private Uri _url = new Uri("http://www.mockrequest.moc/Controller/Action");
public override Uri Url
{
get { return _url; }
}
}
public class MockHttpServerUtilityBase : HttpServerUtilityBase
{
public override string UrlEncode(string s)
{
//return base.UrlEncode(s);
return s; // Not doing anything (this is just a Mock)
}
}
public class MockHttpSession : HttpSessionStateBase
{
// Started with sample http://stackoverflow.com/questions/524457/how-do-you-mock-the-session-object-collection-using-moq
// from http://stackoverflow.com/users/81730/ronnblack
System.Collections.Generic.Dictionary<string, object> _sessionStorage = new System.Collections.Generic.Dictionary<string,object>();
public override object this[string name]
{
get { return _sessionStorage[name]; }
set { _sessionStorage[name] = value; }
}
public override void Add(string name, object value)
{
_sessionStorage[name] = value;
}
}
Вот как я настраиваю Контекст контроллера для использования mocks (MSpec). Это настройка для фактических тестов на contoller (тесты производятся из этого класса)
public abstract class BlahBlahControllerContext
{
protected static BlahBlahController controller;
Establish context = () =>
{
controller = new BlahBlahController();
controller.ControllerContext = new ControllerContext()
{
Controller = controller,
RequestContext = new RequestContext(new MockHttpContext(), new RouteData()),
};
};
}
Далее проиллюстрируем тест (спецификация в мире MSpec), который использует макет сессии:
[Subject("ACCOUNT: Retrieve Password")]
public class retrieve_password_displays_retrieve_password2_page_on_success : BlahBlahControllerContext
{
static ActionResult result;
static RetrievePasswordModel model;
Establish context = () =>
{
model = new RetrievePasswordModel()
{
UserName = "Mike"
};
};
Because of = () =>
{
result = controller.RetrievePassword(model);
};
It should_return_a_RedirectToRouteResult = () =>
{
result.is_a_redirect_to_route_and().action_name().ShouldEqual("RetrievePassword2");
};
It session_should_contain_UN_value = () =>
{
controller.HttpContext.Session["UN"].ShouldEqual("Mike");
};
It session_should_contain_PQ_value = () =>
{
controller.HttpContext.Session["PQ"].ShouldEqual("Question");
};
}
Я понимаю, что это не использует Rhino Mocks. Я надеюсь, что это иллюстрирует принципы, и читатели могут принять его к своим конкретным инструментам и методам.
Если вам нужно создать экземпляр HttpSessionState для тестов устаревших кодов, вы можете использовать механизм FormatterServices для получения неинициализированного объекта. Чтобы получить его работу, необходимо установить личное поле _container, хотя, как и во внутреннем конструкторе
Пример:
var state = (HttpSessionState) System.Runtime.Serialization
.FormatterServices.GetUninitializedObject(typeof(HttpSessionState));
var containerFld = typeof(HttpSessionState).GetField(
"_container", BindingFlags.Instance | BindingFlags.NonPublic);
var itemCollection = new SessionStateItemCollection();
itemCollection["element"] = 1;
containerFld.SetValue(
state,
new HttpSessionStateContainer(
"1",
itemCollection,
new HttpStaticObjectsCollection(),
900,
true,
HttpCookieMode.UseCookies,
SessionStateMode.InProc,
false
)
);
просмотрите классы HttpSessionStateBase
и HttpSessionStateWrapper
в System.Web.Abstractions
. HttpSessionStateBase
- это абстрактный класс, из которого наследуется HttpSessionState
, а HttpSessionStateWrapper
используется для обертывания закрытого класса в абстрактном классе, который вы можете затем высмеять в своих тестах.
Многие классы System.Web
запечатаны (например, HttpSessionState
), так что это настоящая боль, чтобы протестировать ваш код, когда у вас есть методы и классы, которые взаимодействуют с ними. Один шаблон, который я хотел бы использовать, чтобы обойти это, выглядит следующим образом:
public void DoSomething(HttpSessionState state)
{
// take this HttpSeassionState and create an abstract HttpSessionStateBase
// instance
DoSomething(new HttpSessionStateWrapper(state));
}
internal void DoSomething(HttpSessionStateBase state)
{
// my actual logic for working with the session state
}
Открытый метод трудно проверить, потому что HttpSessionState
запечатан, и вы не можете его издеваться. Однако внутренний метод работает с экземпляром HttpSessionStateBase
, который вы можете высмеять. Обратите внимание, что я помечен как внутренний, потому что я не хочу, чтобы внешний мир мог получить доступ к этому методу. Тем не менее, я хочу, чтобы мои тесты имели доступ к этому, поэтому я буду изменять свой AssemblyInfo.cs, чтобы включить что-то вроде этого:
[assembly: InternalsVisibleTo("Vendor.Utilities.Tests")]
Наконец, мой тест для этого будет выглядеть примерно так:
[Test]
public void Test_DoSomething()
{
HttpSessionStateBase state = MockRepository.PartialMock<HttpSessionStateBase>();
state.Expect(s => ...);
MyClass.DoSomething(state);
state.VerifyAllExpectations();
}
Надеюсь, что это поможет. Удачи!
Это то, что я составил на основе других вкладов...
открытый класс MockWebContext {
public Mock<RequestContext> RoutingRequestContext { get; private set; }
public Mock<HttpContextBase> Http { get; private set; }
public Mock<HttpServerUtilityBase> Server { get; private set; }
public Mock<HttpResponseBase> Response { get; private set; }
public Mock<HttpRequestBase> Request { get; private set; }
public Mock<HttpSessionStateBase> Session { get; private set; }
public Mock<ActionExecutingContext> ActionExecuting { get; private set; }
public HttpCookieCollection Cookies { get; private set; }
private IDictionary items;
public MockWebContext()
{
RoutingRequestContext = new Mock<RequestContext>(MockBehavior.Loose);
ActionExecuting = new Mock<ActionExecutingContext>(MockBehavior.Loose);
Http = new Mock<HttpContextBase>(MockBehavior.Loose);
Server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
Response = new Mock<HttpResponseBase>(MockBehavior.Loose);
Request = new Mock<HttpRequestBase>(MockBehavior.Loose);
Session = new Mock<HttpSessionStateBase>(MockBehavior.Loose);
Cookies = new HttpCookieCollection();
items = new Dictionary<string, object>();
RoutingRequestContext.SetupGet(c => c.HttpContext).Returns(Http.Object);
ActionExecuting.SetupGet(c => c.HttpContext).Returns(Http.Object);
Http.SetupGet(c => c.Request).Returns(Request.Object);
Http.SetupGet(c => c.Response).Returns(Response.Object);
Http.SetupGet(c => c.Server).Returns(Server.Object);
Http.SetupGet(c => c.Session).Returns(Session.Object);
Http.SetupGet(c => c.Items).Returns(items);
Request.Setup(c => c.Cookies).Returns(Cookies);
Request.Setup(c => c.RequestContext).Returns(RoutingRequestContext.Object);
Response.Setup(c => c.Cookies).Returns(Cookies);
Session.Setup(c =>
c.Add(It.IsAny<string>(), It.IsAny<object>())
).Callback((string key, object value)=> items.Add(key, value));
Session.Setup(c =>
c.Remove(It.IsAny<string>())
).Callback((string key) => items.Remove(key));
Session.Setup(c =>
c.Clear()
).Callback(() => items.Clear());
Session.Setup(c =>
c[It.IsAny<string>()]
).Returns((string key)=> items[key]);
}
}
Просмотрите проект MvcContrib.