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




web-services unit-testing (11)

मेरे पास एक वेब सेवा है जिसे मैं यूनिट टेस्ट करने की कोशिश कर रहा हूं। सेवा में यह 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 शून्य है।

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


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

इसके अनुसार, यदि आप विधि जोड़ते हैं

    // 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 के माध्यम से स्थापित किया जा सकता है।


एएसपीनेट कोर / एमवीसी 6 आरसी 2 में आप एचटीपी कॉन्टेक्स्ट सेट कर सकते हैं

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://.com/a/34022964/516748

Moq का उपयोग करने पर विचार करें

new Mock<ISession>();

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

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

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


कभी नकली नहीं .. कभी नहीं! समाधान बहुत आसान है। क्यों HttpContext तरह एक सुंदर निर्माण नकली?

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

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

_customObject.SessionProperty("CustomerId")

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

लेकिन जब वास्तविक एप्लिकेशन से बुलाया जाता है, तो _customObject Session का उपयोग करता है।

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

तो परीक्षण सत्र (जमीन के नीचे) सेट कर सकता है और एक विधि को कॉल कर सकता है जैसे कि यह सत्र के बारे में कुछ नहीं जानता है। परीक्षण निर्णय लेता है कि सत्र में कब और क्या संग्रहीत किया जाता है।

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


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

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

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

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

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

सबसे पहले मैंने टेस्ट सत्र वर्ग बनाया:

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

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

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

मिलॉक्स समाधान स्वीकृत एक आईएमएचओ से बेहतर है लेकिन क्वेरीस्ट्रिंग के साथ यूआरएल को संभालने के दौरान मुझे इस कार्यान्वयन के साथ कुछ समस्याएं थीं

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

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 बनाकर "नकली इसे" कर सकते हैं:

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:///", "");
    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();

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

http://codepaste.net/p269t8

ऐसा लगता है कि एमवीसी 5 में काम करता है। मैंने एमवीसी के पुराने संस्करणों में यह कोशिश नहीं की है।

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

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

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 को HttpContext.Current साथ किसी भी कॉल को प्रतिस्थापित करेंगे और उसी विधि तक पहुंच प्राप्त करेंगे। फिर जब आप परीक्षण कर रहे हों, तो आप HttpContextManager तक पहुंच सकते हैं और अपनी अपेक्षाओं का 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());

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





httpcontext