سؤال

أنا جديد تمامًا في الاختبار والسخرية وأحاول كتابة اختبار يضمن أن منطق التحقق الخاص بي يقوم بتعيين أخطاء ModelState بشكل صحيح.

ما أراه هو أن Controller.ControllerContext.HttpContext.Request تم تعيينه في المرة الأولى التي أتحقق فيها منه ولكن في كل مرة بعد ذلك طلب باطل.

يؤدي هذا إلى حدوث استثناء مرجعي فارغ في ملف PopulateDictionary طريقة فئة *ValueProviderDictionary * في مصدر MVC لأنه يتم الوصول إلى كائن الطلب عدة مرات في تلك الطريقة دون التأكد من أن الطلب ليس فارغًا.

أقوم بجمع العديد من التقنيات والمساعدات التي وجدتها أثناء البحث عن كيفية التغلب على بعض المشكلات التي واجهتها حتى الآن، لذا في هذه المرحلة لست متأكدًا إلى حد ما من المكان الذي قد أكون قد قدمت فيه المشكلة.

هل أستخدم كائنات وهمية بشكل غير صحيح هنا؟

اختبار فاشل

//Test
public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel()
{
    //Arrange
    DataAccessFactoryMocks.MockAllDaos();

    var controller = new FooController();

    var testFormCollection = new NameValueCollection();
    testFormCollection.Add("foo.CustomerID", "3");
    testFormCollection.Add("_fooForm", SerializationUtils.Serialize(new FooModel()));

    var mockHttpContext = new MockHttpContext(controller, "POST", testFormCollection, null);

    //Accessor used to run the protected OnActionExecuting method in my controller
    var accessor = new FooControllerAccessor(controller);

    //Request is set, assertion passes
    Assert.IsNotNull(controller.ControllerContext.HttpContext.Request.Form);

    //Request is null when accessing the property a second time, assertion fails
    Assert.IsNotNull(controller.ControllerContext.HttpContext.Request.QueryString);

    //Act
    accessor.OnActionExecuting(new ActionExecutingContext(controller.ControllerContext, MockRepository.GenerateStub<ActionDescriptor>(), new Dictionary<string, object>()));

    //Assert
    Assert.That(controller.ModelState.IsValid == false);
}

مساعد الاختبار

//Test helper to create httpcontext and set controller context accordingly
public class MockHttpContext
{
    public HttpContextBase HttpContext { get; private set; }
    public HttpRequestBase Request { get; private set; }
    public HttpResponseBase Response { get; private set; }
    public RouteData RouteData { get; private set; }

    public MockHttpContext(Controller onController)
    {
        //Setup the common context components and their relationships
        HttpContext = MockRepository.GenerateMock<HttpContextBase>();
        Request = MockRepository.GenerateMock<HttpRequestBase>();
        Response = MockRepository.GenerateMock<HttpResponseBase>();

        //Setup the context, request, response relationship
        HttpContext.Stub(c => c.Request).Return(Request);
        HttpContext.Stub(c => c.Response).Return(Response);

        Request.Stub(r => r.Cookies).Return(new HttpCookieCollection());
        Response.Stub(r => r.Cookies).Return(new HttpCookieCollection());

        Request.Stub(r => r.QueryString).Return(new NameValueCollection());
        Request.Stub(r => r.Form).Return(new NameValueCollection());

        //Apply the context to the suppplied controller
        var rc = new RequestContext(HttpContext, new RouteData());
        onController.ControllerContext = new ControllerContext(rc, onController);
    }

    public MockHttpContext(Controller onController, string httpRequestType, NameValueCollection form, NameValueCollection querystring)
    {
    //Setup the common context components and their relationships
    HttpContext = MockRepository.GenerateMock<HttpContextBase>();
    Request = MockRepository.GenerateMock<HttpRequestBase>();
    Response = MockRepository.GenerateMock<HttpResponseBase>();

    //Setup request type based on parameter value
    Request.Stub(r => r.RequestType).Return(httpRequestType);

    //Setup the context, request, response relationship
    HttpContext.Stub(c => c.Request).Return(Request);
    HttpContext.Stub(c => c.Response).Return(Response);

    Request.Stub(r => r.Cookies).Return(new HttpCookieCollection());
    Response.Stub(r => r.Cookies).Return(new HttpCookieCollection());

    Request.Stub(r => r.QueryString).Return(querystring);
    Request.Stub(r => r.Form).Return(form);

    //Apply the context to the suppplied controller
    var rc = new RequestContext(HttpContext, new RouteData());
    onController.ControllerContext = new ControllerContext(rc, onController);
    }
}

اختبار العمل باستخدام MvcContrib.TestHelper

    public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel()
    {
        //Arrange
        DataAccessFactoryMocks.MockAllDaos();

        TestControllerBuilder builder = new TestControllerBuilder();

        builder.Form.Add("fooModel.CustomerID", "3");

        builder.HttpContext.Request.Stub(r => r.RequestType).Return("POST");

        FooController controller = builder.CreateController<FooController>();

        var accessor = new FooControllerAccessor(controller);

        //Act
        accessor.OnActionExecuting(new ActionExecutingContext(controller.ControllerContext, MockRepository.GenerateStub<ActionDescriptor>(), new Dictionary<string, object>()));

        //Assert
        Assert.IsFalse(controller.ModelState.IsValid);
    }
هل كانت مفيدة؟

المحلول

أود أن أقترح عليك استخدام ممتازة MVCContrib TestHelper لوحدة اختبار وحدات تحكم ASP.NET MVC الخاصة بك باستخدام Rhino Mocks.ستشاهد تبسيطًا جذريًا لاختبارات وحدتك وزيادة إمكانية القراءة.

نصائح أخرى

ما أفهمه من سؤالك هو أنه يمكن أيضًا استبدال الاستهزاء بـ ControllerContext بكائن كعب الروتين لأن الهدف ليس اختبار سلوك ControllerContext.أيضًا، لست متأكدًا حقًا من سبب حاجتك إلى FooControllerAccessor بينما همك الوحيد هو تأكيد ModelState، لذلك تركته هنا:

public void Test_FooController_OnActionExecuting_ShouldMapStateToAFooModel()
{
    // Arrange
    var action = new FooController()
        .Action("index")
        .RequestData(new Dictionary<string, object>()
        {
            {"foo.CustomerID", 3},
            {"_fooForm", new FooModel()}
        });

    //Act
    var modelState = action.ValidateRequest();

    //Assert
    Assert.That(modelState.IsValid == false);
}

لاستخدام هذا الكود يجب عليك تثبيت Xania.AspNet.Simulator (وقت كتابة الإصدار v1.4.0-beta5) يعمل مع Mvc4 وMvc5

مساءً > تثبيت الحزمة Xania.AspNet.Simulator -Pre

لمزيد من الأمثلة راجع ما يلي:

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top