J'ai un service Web que j'essaye de tester. Dans le service, il tire plusieurs valeurs de la HttpContext
comme ceci:
m_password = (string)HttpContext.Current.Session["CustomerId"];
m_userID = (string)HttpContext.Current.Session["CustomerUrl"];
dans le test unitaire, je crée le contexte à l'aide d'une simple requête de travailleur, comme ceci:
SimpleWorkerRequest request = new SimpleWorkerRequest("", "", "", null, new StringWriter());
HttpContext context = new HttpContext(request);
HttpContext.Current = context;
Cependant, chaque fois que j'essaie de définir les valeurs de HttpContext.Current.Session
HttpContext.Current.Session["CustomerId"] = "customer1";
HttpContext.Current.Session["CustomerUrl"] = "customer1Url";
J'obtiens une exception de référence nulle qui dit que HttpContext.Current.Session
est nul.
Est-il possible d'initialiser la session en cours dans le test unitaire?
Nous avons dû nous moquer de HttpContext
en utilisant HttpContextManager
et en appelant l’usine à partir de notre application ainsi que des tests unitaires
public class HttpContextManager
{
private static HttpContextBase m_context;
public static HttpContextBase Current
{
get
{
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;
}
}
Vous pouvez alors remplacer tous les appels à HttpContext.Current
par HttpContextManager.Current
et avoir accès aux mêmes méthodes. Ensuite, lorsque vous testez, vous pouvez également accéder à la HttpContextManager
et vous moquer de vos attentes.
Ceci est un exemple utilisant 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;
}
et ensuite pour l'utiliser dans vos tests unitaires, j'appelle cela dans ma méthode Test Init
HttpContextManager.SetCurrentContext(GetMockedHttpContext());
vous pouvez ensuite, dans la méthode ci-dessus, ajouter les résultats attendus de la session que vous prévoyez être disponibles pour votre service Web.
Vous pouvez "simuler" en créant une nouvelle variable HttpContext
comme ceci:
J'ai pris ce code et l'ai mis sur une classe d'assistance statique comme ceci:
public static HttpContext FakeHttpContext()
{
var httpRequest = new HttpRequest("", "http://stackoverflow/", "");
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;
}
Ou au lieu d'utiliser la réflexion pour construire la nouvelle instance HttpSessionState
, vous pouvez simplement attacher votre HttpSessionStateContainer
à la HttpContext
(selon le commentaire de Brent M. Spell):
SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);
et alors vous pouvez l'appeler dans vos tests unitaires comme:
HttpContext.Current = MockHelper.FakeHttpContext();
La solution Milox est meilleure que celle qui est acceptée à mon humble avis mais J'ai eu quelques problèmes avec cette implémentation lors de la manipulation des URL avec querystring .
J'ai apporté quelques modifications pour que cela fonctionne correctement avec toutes les URL et pour éviter les réflexions.
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;
}
J'ai quelque chose à ce sujet il y a un moment.
Test de l'unité HttpContext.Current.Session dans MVC3 .NET
J'espère que ça aide.
[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(),
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 });
// 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);
}
Si vous utilisez le framework MVC, cela devrait fonctionner. J'ai utilisé Milox's FakeHttpContext et ajouté quelques lignes de code supplémentaires. L'idée est venue de ce post:
Cela semble fonctionner dans MVC 5. Je n'ai pas essayé cela dans les versions précédentes de 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();
Vous pouvez essayer FakeHttpContext :
using (new FakeHttpContext())
{
HttpContext.Current.Session["CustomerId"] = "customer1";
}
La réponse qui a fonctionné avec moi est ce que @ Anthony a écrit, mais vous devez ajouter une autre
request.SetupGet(req => req.Headers).Returns(new NameValueCollection());
afin que vous puissiez utiliser ceci:
HttpContextFactory.Current.Request.Headers.Add(key, value);
Dans asp.net Core/MVC 6 rc2, vous pouvez définir la variable HttpContext
.
var SomeController controller = new SomeController();
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();
rc 1 était
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
Pensez à utiliser Moq
new Mock<ISession>();
Jamais moqueur .. jamais! La solution est assez simple. Pourquoi simuler une si belle création telle que HttpContext
?
Poussez la session! (Cette ligne suffit à la plupart d’entre nous pour comprendre, mais est expliquée en détail ci-dessous)
(string)HttpContext.Current.Session["CustomerId"];
est comment nous y accédons maintenant. Changer ceci en
_customObject.SessionProperty("CustomerId")
Lorsqu'il est appelé à partir de test, _customObject utilise un autre magasin (valeur de clé de base de données ou de nuage [ http://www.kvstore.io/] )
Mais lorsqu'il est appelé depuis l'application réelle, _customObject
utilise Session
.
comment est-ce fait? bien ... Injection de dépendance!
Donc, test peut définir la session (en sous-sol), puis appeler la méthode d'application comme si elle ne connaissait rien de la session. Puis, teste secrètement si le code de l'application a correctement mis à jour la session. Ou si l'application se comporte en fonction de la valeur de session définie par le test.
En fait, nous nous sommes moqués même si j'ai dit: "ne te moque jamais". Parce que nous ne pouvions pas m'empêcher de passer à la règle suivante, "se moque là où ça fait le moins mal!". Se moquant d'énorme HttpContext
ou se moquant d'une petite session, qui fait le moins mal? ne me demandez pas d'où viennent ces règles. Disons simplement le bon sens. Voici une lecture intéressante sur ne pas se moquer car le test unitaire peut nous tuer
Essaye ça:
// 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
Et ajoutez la classe:
public class MockHttpSession : HttpSessionStateBase
{
Dictionary<string, object> _sessionDictionary = new Dictionary<string, object>();
public override object this[string name]
{
get
{
return _sessionDictionary.ContainsKey(name) ? _sessionDictionary[name] : null;
}
set
{
_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);
}
}
}
Cela vous permettra de tester avec session et cache.
Je cherchais quelque chose d'un peu moins invasif que les options mentionnées ci-dessus. À la fin, j’ai proposé une solution de mauvais goût, mais certaines personnes pourraient agir un peu plus vite.
J'ai d'abord créé un TestSession class:
class TestSession : ISession
{
public TestSession()
{
Values = new Dictionary<string, byte[]>();
}
public string Id
{
get
{
return "session_id";
}
}
public bool IsAvailable
{
get
{
return true;
}
}
public IEnumerable<string> Keys
{
get { return Values.Keys; }
}
public Dictionary<string, byte[]> Values { get; set; }
public void Clear()
{
Values.Clear();
}
public Task CommitAsync()
{
throw new NotImplementedException();
}
public Task LoadAsync()
{
throw new NotImplementedException();
}
public void Remove(string key)
{
Values.Remove(key);
}
public void Set(string key, byte[] value)
{
if (Values.ContainsKey(key))
{
Remove(key);
}
Values.Add(key, value);
}
public bool TryGetValue(string key, out byte[] value)
{
if (Values.ContainsKey(key))
{
value = Values[key];
return true;
}
value = new byte[0];
return false;
}
}
Ensuite, j'ai ajouté un paramètre facultatif au constructeur de mon contrôleur. Si le paramètre est présent, utilisez-le pour la manipulation de session. Sinon, utilisez le HttpContext.Session:
class MyController
{
private readonly ISession _session;
public MyController(ISession session = null)
{
_session = session;
}
public IActionResult Action1()
{
Session().SetString("Key", "Value");
View();
}
public IActionResult Action2()
{
ViewBag.Key = Session().GetString("Key");
View();
}
private ISession Session()
{
return _session ?? HttpContext.Session;
}
}
Maintenant je peux injecter mon TestSession dans le contrôleur:
class MyControllerTest
{
private readonly MyController _controller;
public MyControllerTest()
{
var testSession = new TestSession();
var _controller = new MyController(testSession);
}
}
La réponse @Ro Hit m'a beaucoup aidé, mais les informations d'identification de l'utilisateur me manquaient car je devais simuler un utilisateur pour les tests d'unité d'authentification. Par conséquent, permettez-moi de décrire comment je l'ai résolu.
Selon ceci , si vous ajoutez la méthode
// using System.Security.Principal;
GenericPrincipal FakeUser(string userName)
{
var fakeIdentity = new GenericIdentity(userName);
var principal = new GenericPrincipal(fakeIdentity, null);
return principal;
}
et ensuite ajouter
HttpContext.Current.User = FakeUser("myDomain\\myUser");
À la dernière ligne de la méthode TestSetup
que vous avez terminée, les informations d'identification de l'utilisateur sont ajoutées et prêtes à être utilisées pour le test d'authentification.
J'ai également remarqué qu'il y a d'autres parties de HttpContext dont vous pourriez avoir besoin, telles que la méthode .MapPath()
. Il existe un FakeHttpContext disponible, qui est décrit ici et peut être installé via NuGet.
Essayez de cette façon ..
public static HttpContext getCurrentSession()
{
HttpContext.Current = new HttpContext(new HttpRequest("", ConfigurationManager.AppSettings["UnitTestSessionURL"], ""), new HttpResponse(new System.IO.StringWriter()));
System.Web.SessionState.SessionStateUtility.AddHttpSessionStateToContext(
HttpContext.Current, new HttpSessionStateContainer("", new SessionStateItemCollection(), new HttpStaticObjectsCollection(), 20000, true,
HttpCookieMode.UseCookies, SessionStateMode.InProc, false));
return HttpContext.Current;
}
J'ai trouvé la solution simple suivante pour spécifier un utilisateur dans HttpContext: https://forums.asp.net/post/5828182.aspx