Mock HttpContext. Hiện tại trong Phương thức thử nghiệm ban đầu


175

Tôi đang cố gắng thêm thử nghiệm đơn vị vào một ứng dụng ASP.NET MVC mà tôi đã xây dựng. Trong các bài kiểm tra đơn vị của tôi, tôi sử dụng mã sau đây:

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

    ...
}

Với các trợ giúp sau để mô phỏng bối cảnh của bộ điều khiển:

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

Lớp kiểm tra này kế thừa từ một lớp cơ sở có các mục sau:

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

Trong phương thức này, nó gọi một thư viện (mà tôi không có quyền kiểm soát) cố gắng chạy đoạn mã sau:

HttpContext.Current.User.Identity.IsAuthenticated

Bây giờ bạn có thể thấy vấn đề. Tôi đã thiết lập HTTPContext chống lại bộ điều khiển nhưng không phải trong phương thức init cơ sở này. Kiểm tra đơn vị / chế nhạo là rất mới đối với tôi vì vậy tôi muốn chắc chắn rằng tôi làm đúng. Cách chính xác để tôi giả định HTTPContext là gì để nó được chia sẻ trên bộ điều khiển của tôi và bất kỳ thư viện nào được gọi trong phương thức init của tôi.

Câu trả lời:


361

HttpContext.Currenttrả về một thể hiện của System.Web.HttpContext, mà không mở rộng System.Web.HttpContextBase. HttpContextBaseđã được thêm vào sau để giải quyết HttpContextkhó khăn để chế giễu. Hai lớp về cơ bản không liên quan ( HttpContextWrapperđược sử dụng như một bộ chuyển đổi giữa chúng).

May mắn thay, HttpContextbản thân nó có thể thay đổi vừa đủ để bạn thay thế IPrincipal(Người dùng) và IIdentity.

Đoạn mã sau chạy như mong đợi, ngay cả trong một ứng dụng giao diện điều khiển:

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

Chúc mừng nhưng làm thế nào tôi có thể thiết lập điều này cho một người dùng đã đăng xuất?
nfplee

5
@nfplee - Nếu bạn chuyển một chuỗi rỗng vào hàm GenericIdentitytạo, IsAuthenticatedsẽ trả về false
Richard Szalay

2
Điều này có thể được sử dụng để giả lập Cache trong HTTPContext không?
DevDave

1
Vâng, nó có thể. Cảm ơn!
DevDave

4
@CiaranG - Sử dụng MVC HttpContextBase, có thể bị chế giễu. Không cần sử dụng cách giải quyết mà tôi đã đăng nếu bạn đang sử dụng MVC. Nếu bạn tiếp tục với nó, có lẽ bạn cần chạy mã tôi đã đăng trước khi bạn thậm chí tạo bộ điều khiển.
Richard Szalay

34

Dưới đây Test init cũng sẽ thực hiện công việc.

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

Tôi không thể nhận được địa chỉ cho HTTPContext trong một dự án thử nghiệm riêng biệt trong giải pháp của mình. Bạn có thể có được nó thông qua kế thừa một bộ điều khiển?
John Peters

1
Bạn có tham khảo System.Webtrong dự án thử nghiệm của bạn?
PUG

Có nhưng dự án của tôi là một dự án MVC, có thể phiên bản MVC của System.Web chỉ chứa một tập hợp con của không gian tên đó không?
John Peters

2
@ user1522548 (bạn nên tạo một tài khoản) Hội System.Web.dll, v4.0.0.0 chắc chắn có HTTPContext tôi vừa kiểm tra mã nguồn của mình.
PUG

Lỗi của tôi, tôi có một tài liệu tham khảo trong tệp tới System.Web.MVC và KHÔNG System.Web. Cảm ơn bạn đã giúp đỡ.
John Peters

7

Tôi biết đây là một chủ đề cũ hơn, tuy nhiên Mocking một ứng dụng MVC cho các bài kiểm tra đơn vị là điều chúng tôi làm rất thường xuyên.

Tôi chỉ muốn thêm trải nghiệm của mình Mocking một ứng dụng MVC 3 bằng Moq 4 sau khi nâng cấp lên Visual Studio 2013. Không có thử nghiệm đơn vị nào hoạt động ở chế độ gỡ lỗi và HTTPContext đang hiển thị "không thể đánh giá biểu thức" khi cố gắng xem trộm các biến .

Hóa ra visual studio 2013 có vấn đề đánh giá một số đối tượng. Để gỡ lỗi các ứng dụng web bị giả lập hoạt động trở lại, tôi phải kiểm tra "Sử dụng Chế độ tương thích được quản lý" trong Công cụ => Tùy chọn => Gỡ lỗi => Cài đặt chung.

Tôi thường làm một cái gì đó như thế này:

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


}

Và bắt đầu bối cảnh như thế này

FakeHttpContext.SetFakeContext(moController);

Và gọi Phương thức trong bộ điều khiển thẳng

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

Có một lý do chính đáng để đặt nó theo cách này so với câu trả lời được chấp nhận? Điều này có vẻ phức tạp hơn và dường như không cung cấp bất kỳ lợi ích bổ sung nào.
kilkfoe

1
Nó cung cấp một phương pháp mô
phỏng

4

Nếu ứng dụng của bạn bên thứ ba chuyển hướng nội bộ, vì vậy tốt hơn là nên giả lập HTTPContext theo cách dưới đây:

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" } };
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.