سؤال

كيف يمكنني الاختبار أن إجراء جهاز التحكم الخاص بي هو وضع الأخطاء الصحيحة في النداوة عند التحقق من صحة كيان، عندما أستخدم التحقق من صحة DataannoTation في المعاينة MVC 2 1؟

بعض الكود للتوضيح. أولا، الإجراء:

    [HttpPost]
    public ActionResult Index(BlogPost b)
    {
        if(ModelState.IsValid)
        {
            _blogService.Insert(b);
            return(View("Success", b));
        }
        return View(b);
    }

وإليك اختبار وحدة الفشل الذي أعتقد أنه يجب أن يمر ولكن ليس (باستخدام MBUNIT & MOQ):

[Test]
public void When_processing_invalid_post_HomeControllerModelState_should_have_at_least_one_error()
{
    // arrange
    var mockRepository = new Mock<IBlogPostSVC>();
    var homeController = new HomeController(mockRepository.Object);

    // act
    var p = new BlogPost { Title = "test" };            // date and content should be required
    homeController.Index(p);

    // assert
    Assert.IsTrue(!homeController.ModelState.IsValid);
}

أعتقد بالإضافة إلى هذا السؤال، ينبغي أحصل على التحقق من الصحة، ويجب أن أختبره بهذه الطريقة؟

هل كانت مفيدة؟

المحلول

بدلا من المرور في BlogPost يمكنك أيضا إعلان المعلمة الإجراءات FormCollection. وبعد ثم يمكنك إنشاء BlogPost نفسك والدعوة UpdateModel(model, formCollection.ToValueProvider());.

سيؤدي ذلك إلى تشغيل التحقق من الصحة لأي مجال في FormCollection.

    [HttpPost]
    public ActionResult Index(FormCollection form)
    {
        var b = new BlogPost();
        TryUpdateModel(model, form.ToValueProvider());

        if (ModelState.IsValid)
        {
            _blogService.Insert(b);
            return (View("Success", b));
        }
        return View(b);
    }

فقط تأكد من أن الاختبار يضيف قيمة فارغة لكل حقل في نموذج العرض الذي تريد تركه فارغا.

لقد وجدت أن القيام بذلك بهذه الطريقة، على حساب عدد قليل من الخطوط الإضافية من التعليمات البرمجية، يجعل اختبارات وحدتي تشبه الطريقة التي يتم بها استدعاء التعليمات البرمجية في وقت التشغيل مما يجعلها أكثر قيمة. يمكنك أيضا اختبار ما يحدث عندما يدخل شخص ما "ABC" في عنصر تحكم مرتبط بممتلكات INT.

نصائح أخرى

أكره نمرو منشور قديم، لكنني اعتقدت أنني سأضيف أفكاري الخاصة (منذ أن أتيحت للتو هذه المشكلة وركضت عبر هذا المنشور أثناء البحث عن الإجابة).

  1. لا تختبر التحقق من الصحة في اختبارات التحكم الخاصة بك. إما أن تثق في التحقق من صحة MVC أو اكتب بنفسك (أي لا تختبر رمز الآخر، واختبار التعليمات البرمجية الخاصة بك)
  2. إذا كنت ترغب في اختبار التحقق من الصحة يفعل ما تتوقعه، فاختبره في اختبارات النماذج الخاصة بك (أفعل هذا للحصول على صحة أكثر تعقيدا Regex).

ما تريد حقا اختبار هنا هو أن وحدة التحكم الخاصة بك تفعل ما تتوقعه القيام به عند فشل التحقق من الصحة. هذا الكود الخاص بك، وتوقعاتك. اختبار أنه من السهل بمجرد أن تدرك أن كل ما تريد اختباره:

[test]
public void TestInvalidPostBehavior()
{
    // arrange
    var mockRepository = new Mock<IBlogPostSVC>();
    var homeController = new HomeController(mockRepository.Object);
    var p = new BlogPost();

    homeController.ViewData.ModelState.AddModelError("Key", "ErrorMessage"); // Values of these two strings don't matter.  
    // What I'm doing is setting up the situation: my controller is receiving an invalid model.

    // act
    var result = (ViewResult) homeController.Index(p);

    // assert
    result.ForView("Index")
    Assert.That(result.ViewData.Model, Is.EqualTo(p));
}

كنت قد تواجه نفس المشكلة، وبعد قراءة إجابة Pauls والتعليق، بحثت عن طريقة للتحقق من صحة نموذج العرض يدويا.

وجدت هذا البرنامج التعليمي الذي يفسر كيفية التحقق من صحة ViewModel يدويا يستخدم DataBookations. انهم مقتطف رمز رئيسي هو نحو نهاية المنشور.

عدلت الكود قليلا - في البرنامج التعليمي يتم حذف المعلمة الرابعة من tryvalidateObject (valiftateallproperties). من أجل الحصول على كل التعليقات التوضيحية للتحقق من الصحة، يجب تعيين ذلك إلى True.

بالإضافة إلى ذلك، قمت بتعويض الشفرة في طريقة عامة، لإجراء اختبار التحقق من صحة ViewModel بسيطة:

    public static void ValidateViewModel<TViewModel, TController>(this TController controller, TViewModel viewModelToValidate) 
        where TController : ApiController
    {
        var validationContext = new ValidationContext(viewModelToValidate, null, null);
        var validationResults = new List<ValidationResult>();
        Validator.TryValidateObject(viewModelToValidate, validationContext, validationResults, true);
        foreach (var validationResult in validationResults)
        {
            controller.ModelState.AddModelError(validationResult.MemberNames.FirstOrDefault() ?? string.Empty, validationResult.ErrorMessage);
        }
    }

حتى الآن عملت هذا بشكل جيد حقا بالنسبة لنا.

عند استدعاء الأسلوب HomeController.Index في الاختبار الخاص بك، لا تستخدم أي من إطار MVC الذي ينطلق من صحة الصحة حتى يصبح مصدلات. سيكون دائما صحيحا دائما. في التعليمات البرمجية لدينا نسمي طريقة التحقق من الصحة مباشرة في وحدة التحكم بدلا من استخدام التحقق المحيط. لم يكن لدي الكثير من الخبرة مع البيانات (نستخدم nhibernate.validators) ربما يمكن لشخص آخر تقديم إرشادات كيفية الاتصال بالتحقق من صحة من داخل وحدة التحكم الخاصة بك.

كنت أبحث في هذا اليوم ووجدت هذه المدونة post. by Roberto Hernández (MVP) يبدو أنه يوفر أفضل حل لإطلاق موصلات إجراءات تحكم أثناء اختبار الوحدة. سيؤدي ذلك إلى وضع الأخطاء الصحيحة في الموديلات عند التحقق من صحة كيان.

أنا أستخدم الموديلات في حالات الاختبار الخاصة بي لتتمكن من تحديث الطراز. قيمة صالح.

var form = new FormCollection();
form.Add("Name", "0123456789012345678901234567890123456789");

var model = MvcModelBinder.BindModel<AddItemModel>(controller, form);

ViewResult result = (ViewResult)controller.Add(model);

مع MVCModelbinder.bindModel الطريقة التالية (أساسا نفس الرمز المستخدم داخليا في إطار MVC):

        public static TModel BindModel<TModel>(Controller controller, IValueProvider valueProvider) where TModel : class
        {
            IModelBinder binder = ModelBinders.Binders.GetBinder(typeof(TModel));
            ModelBindingContext bindingContext = new ModelBindingContext()
            {
                FallbackToEmptyPrefix = true,
                ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TModel)),
                ModelName = "NotUsedButNotNull",
                ModelState = controller.ModelState,
                PropertyFilter = (name => { return true; }),
                ValueProvider = valueProvider
            };

            return (TModel)binder.BindModel(controller.ControllerContext, bindingContext);
        }

لا يجيب هذا بالضبط على سؤالك، لأنه يتخلى عن البيانات، لكنني سأضيفه لأنه قد يساعد الأشخاص الآخرين في كتابة اختبارات لوحدات التحكم الخاصة بهم:

لديك خيار عدم استخدام التحقق من الصحة المقدمة بواسطة system.componentmodel.dataannotations ولكن لا يزال يستخدم كائن ViewData.modelstate، باستخدام ITS AddModelError الطريقة وبعض آلية التحقق من الصحة الأخرى. على سبيل المثال:

public ActionResult Create(CompetitionEntry competitionEntry)
{        
    if (competitionEntry.Email == null)
        ViewData.ModelState.AddModelError("CompetitionEntry.Email", "Please enter your e-mail");

    if (ModelState.IsValid)
    {
       // insert code to save data here...
       // ...

       return Redirect("/");
    }
    else
    {
        // return with errors
        var viewModel = new CompetitionEntryViewModel();
        // insert code to populate viewmodel here ...
        // ...


        return View(viewModel);
    }
}

هذا لا يزال يتيح لك الاستفادة من Html.ValidationMessageFor() الاشياء التي يولد mvc، دون استخدام DataAnnotations. وبعد عليك أن تتأكد من المفتاح الذي تستخدمه معه AddModelError يطابق ما تتوقع العرض رسائل التحقق من الصحة.

ثم يصبح وحدة التحكم غير قابلة للتحقيق لأن التحقق من الصحة يحدث صراحة، بدلا من الانتهاء من إنتاج MVC بشكل صريح.

أوافق على أن الذراع لديه أفضل إجابة: اختبار سلوك وحدة التحكم الخاصة بك، وليس التحقق من الصحة المدمج.

ومع ذلك، يمكنك أيضا اختبار الوحدات أن الطراز / ViewModel الخاص بك لديه سمات التحقق الصحيحة المعرفة. دعنا نقول أن رأيك يبدو وكأنه هذا:

public class PersonViewModel
{
    [Required]
    public string FirstName { get; set; }
}

سيختبر اختبار الوحدة هذه لوجود [Required] ينسب:

[TestMethod]
public void FirstName_should_be_required()
{
    var propertyInfo = typeof(PersonViewModel).GetProperty("FirstName");

    var attribute = propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), false)
                                .FirstOrDefault();

    Assert.IsNotNull(attribute);
}

على النقيض من الذراع، ليس لدي مشكلة في حفر شديد. حتى هنا اقتراحي. يبني إجابة Giles Smith ويعمل ASP.NET MVC4 (أعرف أن السؤال هو عن MVC 2، لكن Google لا تميز عند البحث عن إجابات ولا يمكنني اختبار MVC2.) بدلا من وضع رمز التحقق في طريقة ثابتة عام، أضعها في وحدة تحكم اختبار. وحدة التحكم لديها كل شيء مطلوب للتحقق من الصحة. لذلك، تبدو وحدة التحكم في الاختبار مثل هذا:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Wbe.Mvc;

protected class TestController : Controller
    {
        public void TestValidateModel(object Model)
        {
            ValidationContext validationContext = new ValidationContext(Model, null, null);
            List<ValidationResult> validationResults = new List<ValidationResult>();
            Validator.TryValidateObject(Model, validationContext, validationResults, true);
            foreach (ValidationResult validationResult in validationResults)
            {
                this.ModelState.AddModelError(String.Join(", ", validationResult.MemberNames), validationResult.ErrorMessage);
            }
        }
    }

بالطبع لا يحتاج الفصل إلى أن تكون innerclass محمية، هذه هي الطريقة التي أستخدمها الآن ولكنني ربما سأعيد استخدام هذه الفئة. إذا كان هناك مكان ما، فهناك MyModel موديل مزين بسمات تشريح بيانات لطيفة، فإن الاختبار يبدو شيئا مثل هذا:

    [TestMethod()]
    public void ValidationTest()
    {
        MyModel item = new MyModel();
        item.Description = "This is a unit test";
        item.LocationId = 1;

        TestController testController = new TestController();
        testController.TestValidateModel(item);

        Assert.IsTrue(testController.ModelState.IsValid, "A valid model is recognized.");
    }

ميزة هذا الإعداد هو أنه يمكنني إعادة استخدام وحدة تحكم الاختبار للاختبارات الخاصة بجميع النماذج الخاصة بي وقد تكون قادرة على تمديدها للسخرية أكثر قليلا حول وحدة التحكم أو استخدام الأساليب المحمية التي تحتوي عليها وحدة تحكم.

آمل أن يساعد.

إذا كنت تهتم بالتحقق من الصحة، لكنك لا تهتم بكيفية تنفيذها، إذا كنت تهتم فقط بالتحقق من صحة طريقة عملك بأعلى مستوى من التجريد، بغض النظر عما إذا كان يتم تطبيقه على أنه استخدام البيانات أو الموديلات أو حتى ActionFilterTattributes يمكنك استخدام حزمة Nuget Xania.aspnt.simulator على النحو التالي:

install-package Xania.AspNet.Simulator

--

var action = new BlogController()
    .Action(c => c.Index(new BlogPost()), "POST");
var modelState = action.ValidateRequest();

modelState.IsValid.Should().BeFalse();

بناء على الإجابة والتعليقات @ Giles-Smith، ل API ويب:

    public static void ValidateViewModel<TViewModel, TController>(this TController controller, TViewModel viewModelToValidate) 
        where TController : ApiController
    {
        var validationContext = new ValidationContext(viewModelToValidate, null, null);
        var validationResults = new List<ValidationResult>();
        Validator.TryValidateObject(viewModelToValidate, validationContext, validationResults, true);
        foreach (var validationResult in validationResults)
        {
            controller.ModelState.AddModelError(validationResult.MemberNames.FirstOrDefault() ?? string.Empty, validationResult.ErrorMessage);
        }
    }

انظر على الجواب تحرير أعلاه ...

@ إجابة جايلز سميث هي نهجي المفضل ولكن يمكن تبسيط التنفيذ:

    public static void ValidateViewModel(this Controller controller, object viewModelToValidate)
    {
        var validationContext = new ValidationContext(viewModelToValidate, null, null);
        var validationResults = new List<ValidationResult>();
        Validator.TryValidateObject(viewModelToValidate, validationContext, validationResults, true);
        foreach (var validationResult in validationResults)
        {
            controller.ModelState.AddModelError(validationResult.MemberNames.FirstOrDefault() ?? string.Empty, validationResult.ErrorMessage);
        }
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top