एक इकाई परीक्षण में HttpContext.Current.Session की स्थापना


185

मेरे पास एक वेब सेवा है जो मैं इकाई परीक्षण की कोशिश कर रहा हूं। सेवा में यह इस HttpContextतरह से कई मान खींचता है:

 m_password = (string)HttpContext.Current.Session["CustomerId"];
 m_userID = (string)HttpContext.Current.Session["CustomerUrl"];

यूनिट टेस्ट में मैं एक साधारण कार्यकर्ता अनुरोध का उपयोग करके संदर्भ बना रहा हूं, जैसे:

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है कि अशक्त है।

क्या इकाई परीक्षण के भीतर वर्तमान सत्र को शुरू करने का कोई तरीका है?



यदि आप कर सकते हैं तो HttpContextBase का उपयोग करें ।
जर्ममेल

जवाबों:


105

हमें अपने आवेदन के साथ-साथ यूनिट टेस्ट से कारखाने HttpContextका उपयोग करके HttpContextManagerऔर कॉल करके मज़ाक करना पड़ा

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;
    }
}

इसके बाद आप के लिए किसी भी कॉल की जगह लेंगे 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;
}

और फिर इसे अपने यूनिट परीक्षणों के भीतर उपयोग करने के लिए, मैं इसे अपने टेस्ट इनिट विधि के भीतर कहता हूं

HttpContextManager.SetCurrentContext(GetMockedHttpContext());

फिर आप उपरोक्त विधि में सत्र से अपेक्षित परिणाम जोड़ सकते हैं जो आप अपनी वेब सेवा के लिए उपलब्ध होने की उम्मीद कर रहे हैं।


1
लेकिन यह
knocte

वह HttpContext का मजाक उड़ाने की कोशिश कर रहा था ताकि उसके SimpleWorkerRequest को HttpContext के मूल्यों तक पहुंच प्राप्त हो, वह अपनी सेवा के भीतर HttpContextFactory का उपयोग करेगा
एंथनी शॉ

क्या यह जानबूझकर किया जाता है कि बैकिंग फ़ील्ड m_context केवल मॉक संदर्भ के लिए लौटाया जाता है (जब SetCurrentContext के माध्यम से सेट किया जाता है) और असली HttpContext के लिए, करंट के लिए हर कॉल के लिए एक आवरण बनाया जाता है?
स्टीफन प्राइस

हाँ यही है। m_context HttpContextBase प्रकार का है और लौटने वाला HttpContextWrapper वर्तमान HttpContext के साथ HttpContextBase देता है
एंथनी शॉ

1
HttpContextManagerइससे बेहतर नाम होगा HttpContextSourceलेकिन मैं मानता हूं HttpContextFactoryकि यह भ्रामक है।
प्रोग्रामिंग

298

आप HttpContextइस तरह एक नया बनाकर "नकली" कर सकते हैं :

http://www.necronet.org/archive/2010/07/28/unit-testing-code-that-uses-httpcontext-current-session.aspx

मैंने वह कोड लिया है और इसे एक स्थिर सहायक वर्ग पर रखा है जैसे:

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(ब्रेंट एम। स्पेल की टिप्पणी के अनुसार):

SessionStateUtility.AddHttpSessionStateToContext(httpContext, sessionContainer);

और फिर आप इसे अपनी इकाई परीक्षणों में कह सकते हैं जैसे:

HttpContext.Current = MockHelper.FakeHttpContext();

24
मुझे यह उत्तर स्वीकार किए गए से बेहतर लगता है क्योंकि आपके परीक्षण गतिविधियों का समर्थन करने के लिए अपना उत्पादन कोड बदलना खराब अभ्यास है। दी गई है, आपके उत्पादन कोड को इस तरह से 3 पार्टी के नामस्थानों को अलग करना चाहिए, लेकिन जब आप विरासत कोड के साथ काम कर रहे होते हैं, तो आपके पास यह नियंत्रण या फिर पुनः कारक के लिए लक्जरी नहीं होता है।
सीन ग्लोवर

29
नए HttpSessionState उदाहरण का निर्माण करने के लिए आपको प्रतिबिंब का उपयोग करने की आवश्यकता नहीं है। आप बस अपने HttpSessionStateContainer को HttpContext में SessionStateUtility.AddHttpSessionStateToContext का उपयोग करके संलग्न कर सकते हैं।
ब्रेंट एम। स्पेल

MockHelper केवल उस वर्ग का नाम है जहां स्थिर विधि है, आप जो भी नाम पसंद करते हैं उसका उपयोग कर सकते हैं।
21

मैंने आपके उत्तर को लागू करने की कोशिश की है लेकिन सत्र अभी भी अशक्त है। क्या आप कृपया मेरे पोस्ट स्टैक्नओवरफ्लो . com/ questions/ 23586765/… पर एक नज़र डालेंगे । धन्यवाद
जो

Server.MapPath()यदि आप इसका उपयोग करते हैं तो यह काम नहीं करेगा।
याकूब

45

Milox समाधान स्वीकार किए गए एक IMHO की तुलना में बेहतर है, लेकिन मुझे इस कार्यान्वयन के साथ कुछ समस्याएं थीं जब querystring के साथ url को संभालना था

मैंने किसी भी यूआरएल के साथ इसे ठीक से काम करने और परावर्तन से बचने के लिए कुछ बदलाव किए।

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;
}

यह आपको नकली httpContext.Session, किसी भी विचार को कैसे करने की अनुमति देता है httpContext.Application?
काइलमिट

39

मैं थोड़ी देर पहले इस बारे में कुछ बोलता हूं।

MVC3 .NET में यूनिट टेस्टिंग HttpContext.Current.Session

आशा करता हूँ की ये काम करेगा।

[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);
}

इतना बढ़िया और सरल काम करता है ... धन्यवाद!
mggSoft

12

यदि आप MVC फ्रेमवर्क का उपयोग कर रहे हैं, तो यह काम करना चाहिए। मैंने Milox के FakeHttpContext का उपयोग किया और कोड की कुछ अतिरिक्त लाइनें जोड़ीं। इस पोस्ट से विचार आया:

http://codepaste.net/p269t8

यह 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();

3
लिंक टूट गया है इसलिए शायद अगली बार यहां कोड डाल दें।
रिहायस

11

आप FakeHttpContext आज़मा सकते हैं :

using (new FakeHttpContext())
{
   HttpContext.Current.Session["CustomerId"] = "customer1";       
}

उपयोग करने के लिए महान और बहुत सरल काम करता है
बीनवा

8

Asp.net Core / MVC 6 rc2 में आप सेट कर सकते हैं HttpContext

var SomeController controller = new SomeController();

controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext();
controller.HttpContext.Session = new DummySession();

आरसी 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>();

7

मेरे साथ काम करने वाले उत्तर में @Anthony ने लिखा था, लेकिन आपको एक और पंक्ति जोड़नी होगी जो है

    request.SetupGet(req => req.Headers).Returns(new NameValueCollection());

तो आप इसका उपयोग कर सकते हैं:

HttpContextFactory.Current.Request.Headers.Add(key, value);

2

इसे इस्तेमाल करे:

        // 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]
    {
        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);
        }
    }
}

यह आपको सत्र और कैश दोनों के साथ परीक्षण करने की अनुमति देगा।


1

मैं ऊपर वर्णित विकल्पों की तुलना में कुछ कम आक्रामक देख रहा था। अंत में मैं एक चुटीले समाधान के साथ आया, लेकिन हो सकता है कि कुछ लोग थोड़ा तेज गति से आगे बढ़ें।

पहले मैंने एक TestSession वर्ग बनाया :

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;
    }
}

फिर मैंने अपने नियंत्रक के निर्माता के लिए एक वैकल्पिक पैरामीटर जोड़ा। यदि पैरामीटर मौजूद है, तो सत्र हेरफेर के लिए इसका उपयोग करें। अन्यथा, 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;
    }
}

अब मैं अपने परीक्षण सत्र को नियंत्रक में इंजेक्ट कर सकता हूं :

class MyControllerTest
{

    private readonly MyController _controller;

    public MyControllerTest()
    {
        var testSession = new TestSession();
        var _controller = new MyController(testSession);
    }
}

मुझे वास्तव में आपका समाधान पसंद है। KISS => यह रखें सरल और बेवकूफ ;-)
CodeNotFound

1

कभी मजाक नहीं .. कभी नहीं! समाधान बहुत सरल है। क्यों इतनी सुंदर रचना नकली HttpContext?

सत्र नीचे धक्का! (बस यह पंक्ति हममें से अधिकांश को समझने के लिए पर्याप्त है लेकिन नीचे विस्तार से बताया गया है)

(string)HttpContext.Current.Session["CustomerId"];अब हम इसे कैसे एक्सेस करते हैं। इसे बदलें

_customObject.SessionProperty("CustomerId")

जब परीक्षण से बुलाया जाता है, तो _customObject वैकल्पिक स्टोर (DB या क्लाउड कुंजी मूल्य [ http://www.kvstore.io/] ) का उपयोग करता है

लेकिन असली एप्लिकेशन से कॉल करने पर, _customObjectउपयोग करता है Session

यह कैसे किया जाता है अच्छा ... निर्भरता इंजेक्शन!

इसलिए परीक्षण सत्र (भूमिगत) सेट कर सकता है और फिर आवेदन विधि को कॉल कर सकता है जैसे कि यह सत्र के बारे में कुछ भी नहीं जानता है। यदि आवेदन कोड सत्र को सही ढंग से अपडेट करता है तो फिर चुपके से जांच करें। या यदि आवेदन परीक्षण द्वारा निर्धारित सत्र मूल्य के आधार पर व्यवहार करता है।

वास्तव में, हमने कहा कि मैंने कभी भी मजाक नहीं किया: "कभी भी मजाक नहीं किया"। बीसेज़ हम मदद नहीं कर सकते थे लेकिन अगले नियम पर खिसक गए, "जहां यह कम से कम चोट करता है!"। बहुत बड़ा HttpContextमजाक करना या एक छोटे सत्र का मजाक उड़ाना, जो कम से कम चोट पहुंचाता है? मुझसे मत पूछो कि ये नियम कहाँ से आए। हमें सिर्फ सामान्य ज्ञान कहना है। यहाँ मज़ाक नहीं पढ़ा जाता है क्योंकि यूनिट टेस्ट हमें मार सकता है


0

जवाब @ मारो मारो ने मुझे बहुत मदद की, लेकिन मैं उपयोगकर्ता क्रेडेंशियल्स को याद कर रहा था क्योंकि मुझे प्रमाणीकरण इकाई परीक्षण के लिए उपयोगकर्ता को नकली करना था। इसलिए, मुझे बताएं कि मैंने इसे कैसे हल किया।

के अनुसार इस , आप विधि जोड़ने अगर

    // using System.Security.Principal;
    GenericPrincipal FakeUser(string userName)
    {
        var fakeIdentity = new GenericIdentity(userName);
        var principal = new GenericPrincipal(fakeIdentity, null);
        return principal;
    }

और फिर संलग्न करें

    HttpContext.Current.User = FakeUser("myDomain\\myUser");

TestSetupआपके द्वारा की गई विधि की अंतिम पंक्ति के लिए , उपयोगकर्ता क्रेडेंशियल्स जोड़े और प्रमाणीकरण परीक्षण के लिए उपयोग किए जाने के लिए तैयार हैं।

मैंने यह भी देखा कि HttpContext में अन्य भाग हैं जिनकी आपको आवश्यकता हो सकती है, जैसे कि .MapPath()विधि। एक FakeHttpContext उपलब्ध है, जो यहां वर्णित है और इसे NuGet के माध्यम से स्थापित किया जा सकता है।



0

इस तरह आजमाएं ..

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;
  }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.