Mock HttpContext.Current in Test Init Method


177

मैं एक ASP.NET MVC अनुप्रयोग जो मैंने बनाया है, में इकाई परीक्षण जोड़ने की कोशिश कर रहा हूँ। मेरी इकाई परीक्षणों में मैं निम्नलिखित कोड का उपयोग करता हूं:

[TestMethod]
public void IndexAction_Should_Return_View() {
    var controller = new MembershipController();
    controller.SetFakeControllerContext("TestUser");

    ...
}

नियंत्रक संदर्भ का मजाक उड़ाने के लिए निम्नलिखित सहायकों के साथ:

public static class FakeControllerContext {
    public static HttpContextBase FakeHttpContext(string username) {
        var context = new Mock<HttpContextBase>();

        context.SetupGet(ctx => ctx.Request.IsAuthenticated).Returns(!string.IsNullOrEmpty(username));

        if (!string.IsNullOrEmpty(username))
            context.SetupGet(ctx => ctx.User.Identity).Returns(FakeIdentity.CreateIdentity(username));

        return context.Object;
    }

    public static void SetFakeControllerContext(this Controller controller, string username = null) {
        var httpContext = FakeHttpContext(username);
        var context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
        controller.ControllerContext = context;
    }
}

यह टेस्ट क्लास एक बेस क्लास से विरासत में मिली है जिसमें निम्नलिखित हैं:

[TestInitialize]
public void Init() {
    ...
}

इस पद्धति के अंदर यह एक पुस्तकालय कहता है (जिस पर मेरा कोई नियंत्रण नहीं है) जो निम्नलिखित कोड को चलाने की कोशिश करता है:

HttpContext.Current.User.Identity.IsAuthenticated

अब आप शायद समस्या देख सकते हैं। मैंने नियंत्रक के खिलाफ नकली HttpContext सेट किया है लेकिन इस बेस इनिट विधि में नहीं। यूनिट परीक्षण / मॉकिंग मेरे लिए बहुत नया है इसलिए मैं यह सुनिश्चित करना चाहता हूं कि मुझे यह अधिकार मिले। मेरे लिए HttpContext को मॉक करने का सही तरीका क्या है ताकि इसे मेरे नियंत्रक और किसी भी लाइब्रेरी में साझा किया जाए जिसे मेरे इनिट विधि में कहा जाता है।

जवाबों:


362

HttpContext.Currentका एक उदाहरण देता है System.Web.HttpContext, जिसका विस्तार नहीं होता है System.Web.HttpContextBaseHttpContextBaseबाद में जोड़ा गया था कि उनका HttpContextमजाक उड़ाना मुश्किल था। दो कक्षाएं मूल रूप से असंबंधित हैं ( HttpContextWrapperइसका उपयोग उनके बीच एक एडॉप्टर के रूप में किया जाता है)।

सौभाग्य से, HttpContextआपके लिए IPrincipal(उपयोगकर्ता) और प्रतिस्थापित करने के लिए केवल पर्याप्त रूप से संभव है IIdentity

कंसोल कोड में भी निम्न कोड अपेक्षित रूप से चलता है:

HttpContext.Current = new HttpContext(
    new HttpRequest("", "http://tempuri.org", ""),
    new HttpResponse(new StringWriter())
    );

// User is logged in
HttpContext.Current.User = new GenericPrincipal(
    new GenericIdentity("username"),
    new string[0]
    );

// User is logged out
HttpContext.Current.User = new GenericPrincipal(
    new GenericIdentity(String.Empty),
    new string[0]
    );

चीयर्स लेकिन मैं इसे एक लॉग आउट उपयोगकर्ता के लिए कैसे सेट कर सकता हूं?
nfplee

5
@nfplee - यदि आप GenericIdentityकंस्ट्रक्टर में एक खाली स्ट्रिंग पास करते हैं , तो IsAuthenticatedवह वापस लौट आएगा
रिचर्ड साज़ले

2
क्या यह HttpContext में कैश को मॉक करने के लिए इस्तेमाल किया जा सकता है?
देववेव

1
हाँ, यह कर सकता है। धन्यवाद!
देववेव

4
@CiaranG - MVC का उपयोग करता है HttpContextBase, जिसका मजाक उड़ाया जा सकता है। यदि आप MVC का उपयोग कर रहे हैं तो मुझे आपके द्वारा पोस्ट किए गए वर्कअराउंड का उपयोग करने की कोई आवश्यकता नहीं है। यदि आप इसके साथ आगे बढ़ते हैं, तो संभवतः आपको नियंत्रक बनाने से पहले मेरे द्वारा पोस्ट किए गए कोड को चलाने की आवश्यकता है ।
रिचर्ड शेजयले 16'13

35

नीचे टेस्ट इनिट भी काम करेगा।

[TestInitialize]
public void TestInit()
{
  HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
  YourControllerToBeTestedController = GetYourToBeTestedController();
}

मुझे अपने समाधान में अलग-अलग टेस्ट प्रोजेक्ट में HTTPContext की एड्रेसबिलिटी नहीं मिल सकती है। क्या आप इसे एक नियंत्रक विरासत के माध्यम से प्राप्त करने में सक्षम हैं?
जॉन पीटर्स

1
क्या आपके पास System.Webअपने परीक्षण प्रोजेक्ट में संदर्भ है ?
पग

हां, लेकिन मेरी परियोजना एक एमवीसी परियोजना है, क्या यह संभव है कि सिस्टम का एमवीसी संस्करण। इसमें केवल उस नामस्थान का सबसेट हो?
जॉन पीटर्स

2
@ user1522548 (आपको एक खाता बनाना चाहिए) विधानसभा System.Web.dll, v4.0.0.0 निश्चित रूप से HTTPContext मैं सिर्फ मेरे स्रोत कोड की जाँच की है।
पुग

मेरी गलती, मेरे पास System.Web.MVC और NOT System.Web की फाइल का संदर्भ है। आपकी सहायता के लिए धन्यवाद।
जॉन पीटर्स

7

मुझे पता है कि यह एक पुराना विषय है, हालांकि यूनिट परीक्षणों के लिए एमवीसी एप्लिकेशन का उपयोग करना कुछ ऐसा है जो हम नियमित रूप से करते हैं।

मैं सिर्फ अपने अनुभवों को जोड़ना चाहता था। Visual Studio 2013 में अपग्रेड करने के बाद Moq 4 का उपयोग करके MVC 3 एप्लिकेशन का उपयोग करना। कोई भी यूनिट परीक्षण डिबग मोड में काम नहीं कर रहा था और HttpContext "अभिव्यक्ति का मूल्यांकन नहीं कर सका" जब चर पर झांकने की कोशिश कर रहा था। ।

दृश्य स्टूडियो 2013 में कुछ वस्तुओं का मूल्यांकन करने वाले मुद्दे हैं। फिर से काम करने वाले वेब एप्लिकेशन को डीबग करना शुरू करने के लिए, मुझे टूल्स => विकल्प => डिबगिंग => सामान्य सेटिंग्स में "उपयोग प्रबंधित संगतता मोड" की जांच करनी थी।

मैं आम तौर पर ऐसा कुछ करता हूं:

public static class FakeHttpContext
{
    public static void SetFakeContext(this Controller controller)
    {

        var httpContext = MakeFakeContext();
        ControllerContext context =
        new ControllerContext(
        new RequestContext(httpContext,
        new RouteData()), controller);
        controller.ControllerContext = context;
    }


    private static HttpContextBase MakeFakeContext()
    {
        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>();

        context.Setup(c=> c.Request).Returns(request.Object);
        context.Setup(c=> c.Response).Returns(response.Object);
        context.Setup(c=> c.Session).Returns(session.Object);
        context.Setup(c=> c.Server).Returns(server.Object);
        context.Setup(c=> c.User).Returns(user.Object);
        user.Setup(c=> c.Identity).Returns(identity.Object);
        identity.Setup(i => i.IsAuthenticated).Returns(true);
        identity.Setup(i => i.Name).Returns("admin");

        return context.Object;
    }


}

और संदर्भ को इस तरह शुरू करना

FakeHttpContext.SetFakeContext(moController);

और कंट्रोलर में मेथड को सीधे आगे की तरफ कॉल करना

long lReportStatusID = -1;
var result = moController.CancelReport(lReportStatusID);

क्या इस तरह से इसे स्वीकार करने का एक अच्छा कारण है? बल्ले से यह अधिक जटिल लगता है और किसी भी अतिरिक्त लाभ की पेशकश नहीं करता है।
किलकेफो

1
यह एक अधिक विस्तृत / मॉड्यूलर
मॉकिंग

4

यदि आपका आवेदन तृतीय पक्ष आंतरिक रूप से पुनर्निर्देशित करता है, तो नीचे दिए गए तरीके से HttpContext का मजाक बनाना बेहतर है:

HttpWorkerRequest initWorkerRequest = new SimpleWorkerRequest("","","","",new StringWriter(CultureInfo.InvariantCulture));
System.Web.HttpContext.Current = new HttpContext(initWorkerRequest);
System.Web.HttpContext.Current.Request.Browser = new HttpBrowserCapabilities();
System.Web.HttpContext.Current.Request.Browser.Capabilities = new Dictionary<string, string> { { "requiresPostRedirectionHandling", "false" } };
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.