จำลอง HttpContext.Current ในวิธีทดสอบ Init


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 ปลอมกับตัวควบคุม แต่ไม่ได้อยู่ในเมธอด Init พื้นฐานนี้ การทดสอบหน่วย / การเยาะเย้ยเป็นสิ่งใหม่สำหรับฉันดังนั้นฉันจึงต้องการให้แน่ใจว่าฉันได้รับสิ่งนี้ถูกต้อง เป็นวิธีที่ถูกต้องสำหรับฉันที่จะเยาะเย้ย HttpContext เพื่อที่จะใช้ร่วมกันทั่วตัวควบคุมของฉันและห้องสมุดใด ๆ ที่เรียกว่าในวิธีการเริ่มต้นของฉัน

คำตอบ:


362

HttpContext.Currentผลตอบแทนที่ได้ตัวอย่างของซึ่งไม่ขยายSystem.Web.HttpContext เพิ่มในภายหลังเพื่อที่อยู่ยากที่จะเยาะเย้ย โดยทั่วไปคลาสที่สองจะไม่เกี่ยวข้องกัน ( ใช้เป็นอะแดปเตอร์ระหว่างพวกเขา)System.Web.HttpContextBaseHttpContextBaseHttpContextHttpContextWrapper

โชคดีที่HttpContextตัวเองเป็น fakeable พอเพียงสำหรับการที่คุณทำมาแทนที่IPrincipal(User) 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 - หากคุณส่งสตริงว่างเข้าไปในGenericIdentityconstructor IsAuthenticatedจะส่งคืน false
Richard Szalay

2
สิ่งนี้สามารถใช้ในการจำลองแคชใน HttpContext ได้หรือไม่
DevDave

1
ใช่มันทำได้ ขอบคุณ!
DevDave

4
@CiaranG - MVC ใช้HttpContextBaseซึ่งสามารถเยาะเย้ย ไม่จำเป็นต้องใช้วิธีแก้ปัญหาที่ฉันโพสต์หากคุณใช้ MVC หากคุณไปข้างหน้าด้วยคุณอาจต้องเรียกใช้รหัสที่ฉันโพสต์ก่อนที่คุณจะสร้างคอนโทรลเลอร์
Richard Szalay

35

ด้านล่าง Test Init จะทำงานเช่นกัน

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

ฉันไม่สามารถรับแอดเดรส HTTPContext ในโครงการทดสอบแยกต่างหากในโซลูชัน คุณสามารถรับมันผ่านการสืบทอดคอนโทรลเลอร์หรือไม่?
John Peters

1
คุณมีการอ้างอิงถึงSystem.Webในโครงการทดสอบของคุณ?
PUG

ใช่ แต่โครงการของฉันเป็นโครงการ MVC เป็นไปได้หรือไม่ว่ารุ่น MVC ของ System.Web มีเฉพาะส่วนย่อยของเนมสเปซนั้นหรือไม่
John Peters

2
@ user1522548 (คุณควรสร้างบัญชี) Assembly System.Web.dll, v4.0.0.0 มี HTTPContext แน่นอนฉันเพิ่งตรวจสอบซอร์สโค้ดของฉัน
PUG

ความผิดพลาดของฉันฉันมีการอ้างอิงในไฟล์ไปที่ System.Web.MVC และไม่ใช่ System.Web ขอบคุณสำหรับความช่วยเหลือของคุณ.
John Peters

7

ฉันรู้ว่านี่เป็นวิชาที่เก่ากว่า แต่การเยาะเย้ยแอปพลิเคชัน MVC สำหรับการทดสอบหน่วยเป็นสิ่งที่เราทำอยู่เป็นประจำ

ฉันแค่ต้องการเพิ่มประสบการณ์ของฉันการเยาะเย้ยแอปพลิเคชัน MVC 3 โดยใช้ Moq 4 หลังจากอัปเกรดเป็น Visual Studio 2013 ไม่มีการทดสอบหน่วยใดทำงานในโหมดดีบักและ HttpContext แสดงว่า "ไม่สามารถประเมินนิพจน์" เมื่อพยายามดูตัวแปร .

ปรากฎว่า visual studio 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);

มีเหตุผลที่ดีที่จะตั้งเป็นแบบนี้กับคำตอบที่ยอมรับหรือไม่? ค้างคาวดูเหมือนว่าจะซับซ้อนกว่านี้และดูเหมือนจะไม่ได้รับประโยชน์ใด ๆ เพิ่มเติม
kilkfoe

1
มันเสนอวิธีการเยาะเย้ยรายละเอียดเพิ่มเติม / โมดูลาร์
Vincent Buscarello

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" } };
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.