|
|
爱看球的人字拖 · js中下拉框如何删除一个选项 - ...· 1 年前 · |
|
|
愤怒的佛珠 · jstack+jdb命令查看线程及死锁堆栈信 ...· 2 年前 · |
|
|
阳刚的牛排 · Comparing ...· 2 年前 · |
|
|
不羁的投影仪 · 非参数检验的适用条件样本量-掘金· 2 年前 · |
我有一个正在尝试进行单元测试的web服务。在服务中,它从
HttpContext
中提取几个值,如下所示:
m_password = (string)HttpContext.Current.Session["CustomerId"];
m_userID = (string)HttpContext.Current.Session["CustomerUrl"];
在单元测试中,我使用一个简单的worker请求创建上下文,如下所示:
SimpleWorkerRequest request = new SimpleWorkerRequest("", "", "", null, new StringWriter());
HttpContext context = new HttpContext(request);
HttpContext.Current = context;
但是,每当我尝试设置
HttpContext.Current.Session
的值时,
HttpContext.Current.Session["CustomerId"] = "customer1";
HttpContext.Current.Session["CustomerUrl"] = "customer1Url";
我得到了空引用异常,表明
HttpContext.Current.Session
为空。
有没有办法在单元测试中初始化当前会话?
我们必须通过使用
HttpContextManager
并在应用程序和单元测试中调用工厂来模拟
HttpContext
public class HttpContextManager
private static HttpContextBase m_context;
public static HttpContextBase Current
if (m_context != null)
return m_context;
if (HttpContext.Current == null)
throw new InvalidOperationException("HttpContext not available");
return new HttpContextWrapper(HttpContext.Current);
public static void SetCurrentContext(HttpContextBase context)
m_context = context;
}
然后,您可以将对
HttpContext.Current
的任何调用替换为
HttpContextManager.Current
,并可以访问相同的方法。然后,在进行测试时,您还可以访问
HttpContextManager
并模拟您的期望
这是一个使用 Moq 的示例
private HttpContextBase GetMockedHttpContext()
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>();
var urlHelper = new Mock<UrlHelper>();
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
var requestContext = new Mock<RequestContext>();
requestContext.Setup(x => x.HttpContext).Returns(context.Object);
context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);
context.Setup(ctx => ctx.User).Returns(user.Object);
user.Setup(ctx => ctx.Identity).Returns(identity.Object);
identity.Setup(id => id.IsAuthenticated).Returns(true);
identity.Setup(id => id.Name).Returns("test");
request.Setup(req => req.Url).Returns(new Uri("http://www.google.com"));
request.Setup(req => req.RequestContext).Returns(requestContext.Object);
requestContext.Setup(x => x.RouteData).Returns(new RouteData());
request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
return context.Object;
}
然后,为了在单元测试中使用它,我在我的Test Init方法中调用了它
HttpContextManager.SetCurrentContext(GetMockedHttpContext());
然后,您可以在上面的方法中添加您期望对您的web服务可用的会话的预期结果。
您可以通过创建一个新
HttpContext
来“伪造它”,如下所示:
我将该代码放在一个静态助手类中,如下所示:
public static HttpContext FakeHttpContext()
var httpRequest = new HttpRequest("", "http://example.com/", "");
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
new HttpStaticObjectsCollection(), 10, true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null, CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
return httpContext;
}
或者,不使用反射来构造新的
HttpSessionState
实例,您可以直接将
HttpSessionStateContainer
附加到
HttpContext
(根据Brent M.Spell的评论):
SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);
然后你可以在你的单元测试中调用它,就像这样:
HttpContext.Current = MockHelper.FakeHttpContext();
我不久前写了一些关于这方面的东西。
Unit Testing HttpContext.Current.Session in MVC3 .NET
希望能有所帮助。
[TestInitialize]
public void TestSetup()
// We need to setup the Current HTTP Context as follows:
// Step 1: Setup the HTTP Request
var httpRequest = new HttpRequest("", "http://localhost/", "");
// Step 2: Setup the HTTP Response
var httpResponce = new HttpResponse(new StringWriter());
// Step 3: Setup the Http Context
var httpContext = new HttpContext(httpRequest, httpResponce);
var sessionContainer =
new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc,
false);
httpContext.Items["AspSession"] =
typeof(HttpSessionState)
.GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
CallingConventions.Standard,
new[] { typeof(HttpSessionStateContainer) },
null)
.Invoke(new object[] { sessionContainer });
// Step 4: Assign the Context
HttpContext.Current = httpContext;
[TestMethod]
public void BasicTest_Push_Item_Into_Session()
// Arrange
var itemValue = "RandomItemValue";
var itemKey = "RandomItemKey";
// Act
HttpContext.Current.Session.Add(itemKey, itemValue);
// Assert
Assert.AreEqual(HttpContext.Current.Session[itemKey], itemValue);
}
除 I had some problems with this implementation when handling urls with querystring 外, Milox solution 优于公认的IMHO。
我做了一些修改,以使它能与任何urls正常工作,并避免反射。
public static HttpContext FakeHttpContext(string url)
var uri = new Uri(url);
var httpRequest = new HttpRequest(string.Empty, uri.ToString(),
uri.Query.TrimStart('?'));
var stringWriter = new StringWriter();
var httpResponse = new HttpResponse(stringWriter);
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("id",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
10, true, HttpCookieMode.AutoDetect,
SessionStateMode.InProc, false);
SessionStateUtility.AddHttpSessionStateToContext(
httpContext, sessionContainer);
return httpContext;
}
如果你使用的是MVC框架,这应该是可行的。我使用了 Milox's FakeHttpContext并添加了几行额外的代码。这个想法来自于这篇文章:
这似乎在MVC 5中起作用,我还没有在MVC的早期版本中尝试过。
HttpContext.Current = MockHttpContext.FakeHttpContext();
var wrapper = new HttpContextWrapper(HttpContext.Current);
MyController controller = new MyController();
controller.ControllerContext = new ControllerContext(wrapper, new RouteData(), controller);
string result = controller.MyMethod();
你可以试试 FakeHttpContext
using (new FakeHttpContext())
HttpContext.Current.Session["CustomerId"] = "customer1";
}
试试这个:
// MockHttpSession Setup
var session = new MockHttpSession();
// MockHttpRequest Setup - mock AJAX request
var httpRequest = new Mock<HttpRequestBase>();
// Setup this part of the HTTP request for AJAX calls
httpRequest.Setup(req => req["X-Requested-With"]).Returns("XMLHttpRequest");
// MockHttpContextBase Setup - mock request, cache, and session
var httpContext = new Mock<HttpContextBase>();
httpContext.Setup(ctx => ctx.Request).Returns(httpRequest.Object);
httpContext.Setup(ctx => ctx.Cache).Returns(HttpRuntime.Cache);
httpContext.Setup(ctx => ctx.Session).Returns(session);
// MockHttpContext for cache
var contextRequest = new HttpRequest("", "http://localhost/", "");
var contextResponse = new HttpResponse(new StringWriter());
HttpContext.Current = new HttpContext(contextRequest, contextResponse);
// MockControllerContext Setup
var context = new Mock<ControllerContext>();
context.Setup(ctx => ctx.HttpContext).Returns(httpContext.Object);
//TODO: Create new controller here
// Set controller's ControllerContext to context.Object
并添加类:
public class MockHttpSession : HttpSessionStateBase
Dictionary<string, object> _sessionDictionary = new Dictionary<string, object>();
public override object this[string name]
return _sessionDictionary.ContainsKey(name) ? _sessionDictionary[name] : null;
_sessionDictionary[name] = value;
public override void Abandon()
var keys = new List<string>();
foreach (var kvp in _sessionDictionary)
keys.Add(kvp.Key);
foreach (var key in keys)
_sessionDictionary.Remove(key);
public override void Clear()
var keys = new List<string>();
foreach (var kvp in _sessionDictionary)
keys.Add(kvp.Key);
foreach(var key in keys)
_sessionDictionary.Remove(key);
}
这将允许您同时使用会话和缓存进行测试。
在asp.net核心/MVC6 rc2中,您可以设置
HttpContext
var SomeController controller = new SomeController();
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();
rc 1是
var SomeController controller = new SomeController();
controller.ActionContext = new ActionContext();
controller.ActionContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();
https://stackoverflow.com/a/34022964/516748
考虑使用
Moq
new Mock<ISession>();
我找到了以下在HttpContext中指定用户的简单解决方案: https://forums.asp.net/post/5828182.aspx
永远不要嘲笑..绝不可能!解决方案非常简单。为什么要伪造像
HttpContext
这样美丽的作品呢?
将会话向下推!(这一行对于我们大多数人来说已经足够理解了,但下面会详细解释)
(string)HttpContext.Current.Session["CustomerId"];
是我们现在访问它的方式。将其更改为
_customObject.SessionProperty("CustomerId")
当从测试中调用时,_customObject使用替代存储(DB或云密钥值 [http://www.kvstore.io/] )
但是当从实际应用程序调用时,
_customObject
使用
Session
。
这是怎么做的?好吧..。依赖注入!
因此test可以设置会话(在地下),然后调用应用程序方法,就好像它对会话一无所知一样。然后测试秘密地检查应用程序代码是否正确地更新了会话。或者应用程序是否基于测试设置的会话值运行。
实际上,尽管我说过:“永远不要嘲笑”,但我们最终还是被嘲笑了。因为我们不由自主地滑到了下一条规则:“嘲笑伤害最小的地方!”嘲笑巨大的
HttpContext
和嘲笑一个很小的会话,哪个伤害最小?不要问我这些规则是从哪里来的。让我们只说常识。这里有一个关于不嘲笑
as unit test can kills us
的有趣的读物
试试这个方法..
public static HttpContext getCurrentSession()
|
|
阳刚的牛排 · Comparing SELECT..INTO and CTAS use cases in Azure SQL Data Warehouse | Azure Blog | Microsoft Azure 2 年前 |
|
|
不羁的投影仪 · 非参数检验的适用条件样本量-掘金 2 年前 |