วิธีการจำลองคำร้องขอบนคอนโทรลเลอร์ใน ASP.Net MVC


171

ฉันมีคอนโทรลเลอร์ใน C # โดยใช้เฟรมเวิร์ก ASP.Net MVC

public class HomeController:Controller{
  public ActionResult Index()
    {
      if (Request.IsAjaxRequest())
        { 
          //do some ajaxy stuff
        }
      return View("Index");
    }
}

ฉันได้รับคำแนะนำเกี่ยวกับการเยาะเย้ยและหวังว่าจะทดสอบโค้ดด้วย RhinoMocks ต่อไปนี้

var mocks = new MockRepository();
var mockedhttpContext = mocks.DynamicMock<HttpContextBase>();
var mockedHttpRequest = mocks.DynamicMock<HttpRequestBase>();
SetupResult.For(mockedhttpContext.Request).Return(mockedHttpRequest);

var controller = new HomeController();
controller.ControllerContext = new ControllerContext(mockedhttpContext, new RouteData(), controller);
var result = controller.Index() as ViewResult;
Assert.AreEqual("About", result.ViewName);

อย่างไรก็ตามฉันได้รับข้อผิดพลาดนี้:

ข้อยกเว้น System.ArgumentNullException: System.ArgumentNullException: ค่าต้องไม่เป็นค่าว่าง ชื่อพารามิเตอร์: คำขอที่ System.Web.Mvc.AjaxRequestExtensions.IsAjaxRequest (คำขอ HttpRequestBase)

เนื่องจากRequestวัตถุบนคอนโทรลเลอร์ไม่มี setter ฉันพยายามทำให้การทดสอบนี้ทำงานอย่างถูกต้องโดยใช้รหัสที่แนะนำจากคำตอบด้านล่าง

สิ่งนี้ใช้ Moq แทน RhinoMocks และในการใช้ Moq ฉันใช้สิ่งต่อไปนี้สำหรับการทดสอบเดียวกัน:

var request = new Mock<HttpRequestBase>();
// Not working - IsAjaxRequest() is static extension method and cannot be mocked
// request.Setup(x => x.IsAjaxRequest()).Returns(true /* or false */);
// use this
request.SetupGet(x => x.Headers["X-Requested-With"]).Returns("XMLHttpRequest");

var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);
var controller = new HomeController(Repository, LoginInfoProvider);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
var result = controller.Index() as ViewResult;
Assert.AreEqual("About", result.ViewName);

แต่รับข้อผิดพลาดต่อไปนี้:

ข้อยกเว้น System.ArgumentException: System.ArgumentException: การตั้งค่าที่ไม่ถูกต้องในสมาชิกที่ไม่ใช่ overridable: x => x.Headers ["X- ร้องขอด้วย"] ที่ Moq.MockThrowIfCantOverride (การตั้งค่านิพจน์เมธอดInfo)

ดูเหมือนว่าฉันไม่สามารถตั้งค่าส่วนหัวคำขอได้อีก ฉันจะตั้งค่านี้ใน RhinoMocks หรือ Moq ได้อย่างไร


แทนที่ Request.IsAjaxRequest ด้วย Request.IsAjaxRequest ()
eu-ge-ne

Mock Request.Headers ["X-Requested-With"] หรือ Request ["X-Requested-With"] แทน Request.IsAjaxRequest () ฉันได้อัปเดตคำถามของฉันแล้ว
eu-ge-ne

คำตอบ:


212

ใช้ขั้นต่ำ :

var request = new Mock<HttpRequestBase>();
// Not working - IsAjaxRequest() is static extension method and cannot be mocked
// request.Setup(x => x.IsAjaxRequest()).Returns(true /* or false */);
// use this
request.SetupGet(x => x.Headers).Returns(
    new System.Net.WebHeaderCollection {
        {"X-Requested-With", "XMLHttpRequest"}
    });

var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);

var controller = new YourController();
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);

UPDATED:

จำลองRequest.Headers["X-Requested-With"]หรือแทนRequest["X-Requested-With"]Request.IsAjaxRequest()


1
ฉันได้รับข้อความ "อาร์กิวเมนต์ Type สำหรับเมธอด 'ISetupGetter <T, TProperty> Moq.Mock <T> .SetupGet <Tpropert> .... ไม่สามารถอนุมานจาก uage ได้ลองระบุอาร์กิวเมนต์ประเภทอย่างชัดเจนฉันตั้งประเภทใด 'var request =' ถึงจะใช้งานได้หรือไม่
นิสสัน

เพิ่งอัปเดตคำตอบของฉัน - ไม่ใช่ Request.IsAjaxRequest แต่ Request.IsAjaxRequest () อัปเดตคำถามของคุณด้วย
eu-ge-ne

ยังคงสร้าง: ข้อยกเว้น System.ArgumentException: System.ArgumentException: การตั้งค่าที่ไม่ถูกต้องในสมาชิกที่ไม่ใช่ overridable: x => x.IsAjaxRequest () ที่ Moq.Mock.ThrowIfCantOverride (การตั้งค่าการแสดงออก methodInfo)
นิสสัน

ปัญหาคือว่า IsAjaxRequest () เป็นวิธีการขยายแบบคงที่และไม่สามารถล้อเลียน - ฉันได้ปรับปรุงคำตอบของฉัน
eu-ge-ne

ควรเป็น context.SetupGet (x => x.Request). Return (request.Object); รหัสของคุณด้านบนไม่มีเครื่องหมาย 's' ในการส่งคืนยังส่งผลให้เกิดข้อยกเว้น System.ArgumentException: System.ArgumentException: การตั้งค่าไม่ถูกต้องสำหรับสมาชิกที่ไม่สามารถ overridable: x => x.Headers ["X-Requested-With"] ที่ Moq .Mock.ThrowIfCantOverride (การตั้งค่านิพจน์วิธี MethodInfo) ข้อความแสดงข้อผิดพลาด
นิสสัน

17

สำหรับทุกคนที่ใช้ NSubstitute ฉันสามารถแก้ไขคำตอบข้างต้นและทำสิ่งนี้ ... (โดยรายละเอียดคือชื่อวิธีการดำเนินการบนคอนโทรลเลอร์)

 var fakeRequest = Substitute.For<HttpRequestBase>();
        var fakeContext = Substitute.For<HttpContextBase>();
        fakeRequest.Headers.Returns(new WebHeaderCollection { {"X-Requested-With", "XMLHttpRequest"}});
        fakeContext.Request.Returns(fakeRequest);
        controller.ControllerContext = new ControllerContext(fakeContext, new RouteData(), controller);
        var model = new EntityTypeMaintenanceModel();

        var result = controller.Details(model) as PartialViewResult;

        Assert.IsNotNull(result);
        Assert.AreEqual("EntityType", result.ViewName);

13

นี่คือวิธีการแก้ปัญหาการทำงานโดยใช้ RhinoMocks ฉันใช้มันเป็นวิธีการแก้ปัญหา Moq ฉันพบที่http://thegrayzone.co.uk/blog/2010/03/mocking-request-isajaxrequest/

public static void MakeAjaxRequest(this Controller controller)
{
        MockRepository mocks = new MockRepository();

        // Create mocks
        var mockedhttpContext = mocks.DynamicMock<HttpContextBase>();
        var mockedHttpRequest = mocks.DynamicMock<HttpRequestBase>();

        // Set headers to pretend it's an Ajax request
        SetupResult.For(mockedHttpRequest.Headers)
            .Return(new WebHeaderCollection() {
                {"X-Requested-With", "XMLHttpRequest"}
            });

        // Tell the mocked context to return the mocked request
        SetupResult.For(mockedhttpContext.Request).Return(mockedHttpRequest);

        mocks.ReplayAll();

        // Set controllerContext
        controller.ControllerContext = new ControllerContext(mockedhttpContext, new RouteData(), controller);
}

5

เป็น AjaxRequest เป็นวิธีการขยาย ดังนั้นคุณสามารถทำได้ด้วยวิธีต่อไปนี้โดยใช้แรด:

    protected HttpContextBase BuildHttpContextStub(bool isAjaxRequest)
    {
        var httpRequestBase = MockRepository.GenerateStub<HttpRequestBase>();   
        if (isAjaxRequest)
        {
            httpRequestBase.Stub(r => r["X-Requested-With"]).Return("XMLHttpRequest");
        }

        var httpContextBase = MockRepository.GenerateStub<HttpContextBase>();
        httpContextBase.Stub(c => c.Request).Return(httpRequestBase);

        return httpContextBase;
    }

    // Build controller
    ....
    controller.ControllerContext = new ControllerContext(BuildHttpContextStub(true), new RouteData(), controller);

4

ดูเหมือนว่าคุณกำลังมองหาสิ่งนี้

 var requestMock = new Mock<HttpRequestBase>();
 requestMock.SetupGet(rq => rq["Age"]).Returns("2001");

การใช้งานในคอนโทรลเลอร์:

 public ActionResult Index()
 {
        var age = Request["Age"]; //This will return 2001
 }

3

คุณต้องจำลอง HttpContextBase และใส่ไว้ในคุณสมบัติ ControllerContext ของคุณเช่นนั้น:

controller.ControllerContext = 
new ControllerContext(mockedHttpContext, new RouteData(), controller);

และ mockedHttpContext อะไรที่ต้องถูกเยาะเย้ย? วัตถุ tje RequestContext จำเป็นต้องมีวัตถุ HttpContextBase () ในตัวสร้างและ HttpContextBase () ไม่มีตัวสร้างที่ยอมรับพารามิเตอร์เป็นศูนย์
นิสสัน

ฉันพยายาม: var mocks = new MockRepository (); var mockedhttpContext = mocks.DynamicMock <HttpContextBase> (); var mockedHttpRequest = mocks.DynamicMock <HttpRequestBase> (); SetupResult.For (mockedhttpContext.Request) .Return (mockedHttpRequest); var controller = HomeController ใหม่ (Repository, LoginInfoProvider); controller.ControllerContext = ใหม่ mockedhttpContext, ใหม่ RouteData (), คอนโทรลเลอร์); var result = controller.Index () เป็น ViewResult; อย่างไรก็ตามยังคงได้รับข้อยกเว้นเดียวกันโยน
นิสสัน

ลิงก์ของคุณใช้งานไม่ได้ แต่ข้อความต่อไปนี้จะใช้งานได้ _request.Setup (o => o.Form). ส่งกลับ (ใหม่ NameValueCollection ());
Vdex

2

ในIsAjaxRequest()การส่งคืนค่าเท็จในระหว่างการทดสอบหน่วยคุณจำเป็นต้องตั้งค่าส่วนหัวคำขอรวมทั้งขอค่าการรวบรวมทั้งในวิธีการทดสอบตามที่ระบุด้านล่าง:

_request.SetupGet(x => x.Headers).Returns(new System.Net.WebHeaderCollection { { "X-Requested-With", "NotAjaxRequest" } });
_request.SetupGet(x=>x["X-Requested-With"]).Returns("NotAjaxRequest");

เหตุผลในการตั้งค่าทั้งสองถูกซ่อนอยู่ในการใช้งาน IsAjaxRequest () ซึ่งได้รับด้านล่าง:

public static bool IsAjaxRequest(this HttpRequestBase request)<br/>
{ 
    if (request == null)
    {
        throw new ArgumentNullException("request");
    }
    return ((request["X-Requested-With"] == "XMLHttpRequest") || ((request.Headers != null) && (request.Headers["X-Requested-With"] == "XMLHttpRequest")));
}

มันใช้ทั้งชุดคำขอและส่วนหัวนี่คือเหตุผลที่เราต้องสร้างการตั้งค่าสำหรับส่วนหัวและชุดคำขอ

สิ่งนี้จะทำให้คำขอคืนค่าเท็จเมื่อไม่ใช่คำขอ ajax เพื่อทำให้มันกลับมาเป็นจริงคุณสามารถทำสิ่งต่อไปนี้:

_httpContext.SetupGet(x => x.Request["X-Requested-With"]).Returns("XMLHttpRequest");

0

ฉันพบวิธีอื่นในการเพิ่มวัตถุ HttpRequestMessage ในคำขอของคุณระหว่าง Web API ดังต่อไปนี้

[Test]
public void TestMethod()
{
    var controllerContext = new HttpControllerContext();
    var request = new HttpRequestMessage();
    request.Headers.Add("TestHeader", "TestHeader");
    controllerContext.Request = request;
    _controller.ControllerContext = controllerContext;

    var result = _controller.YourAPIMethod();
    //Your assertion
}

0

(สายไปงานเลี้ยง แต่ฉันไปสำหรับเส้นทางอื่นดังนั้นคิดว่าฉันจะแบ่งปัน)

เพื่อไปที่บริสุทธิ์รหัส / วิธีการเยาะเย้ยของการทดสอบนี้โดยไม่ต้องสร้าง mocks สำหรับชั้นเรียน Http ฉันใช้ IControllerHelper ซึ่งมีวิธีการเริ่มต้นซึ่งใช้การร้องขอเป็นพารามิเตอร์แล้วสัมผัสคุณสมบัติที่ฉันต้องการเช่น:

    public interface IControllerHelper
    {
        void Initialise(HttpRequest request);
        string HostAddress { get; }
    }

    public class ControllerHelper : IControllerHelper
    {
        private HttpRequest _request;
        
        public void Initialise(HttpRequest request)
        {
            _request = request;
        }

        public string HostAddress =>  _request.GetUri().GetLeftPart(UriPartial.Authority);
    }

จากนั้นในตัวควบคุมของฉันฉันเรียก initialise ที่จุดเริ่มต้นของวิธีการ:

        _controllerHelper.Initialise(Request);

และจากนั้นรหัสของฉันก็ขึ้นอยู่กับการพึ่งพาซึ่งเยาะเย้ยเท่านั้น

        return Created(new Uri($"{_controllerHelper.HostName}/api/MyEndpoint/{result.id}"), result);

สำหรับการทดสอบการใช้งานฉันเพียงแค่แทนที่ iControllerHelper ในองค์ประกอบเพื่อทดแทน

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