Pertanyaan Tes unit pada validasi MVC


Bagaimana saya bisa menguji bahwa tindakan pengontrol saya menempatkan kesalahan yang benar dalam ModelState ketika memvalidasi entitas, ketika saya menggunakan validasi DataAnnotation di MVC 2 Pratinjau 1?

Beberapa kode untuk diilustrasikan. Pertama, aksi:

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

Dan inilah tes unit gagal yang saya pikir harus lulus tetapi tidak (menggunakan 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);
}

Saya kira selain pertanyaan ini, harus Saya sedang menguji validasi, dan haruskah saya mengujinya dengan cara ini?


75
2017-08-13 02:20


asal


Jawaban:


Alih-alih lewat dalam BlogPost Anda juga dapat mendeklarasikan parameter tindakan sebagai FormCollection. Kemudian Anda dapat membuat BlogPost diri Anda dan hubungi UpdateModel(model, formCollection.ToValueProvider());.

Ini akan memicu validasi untuk setiap bidang di 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);
    }

Pastikan saja tes Anda menambahkan nilai nol untuk setiap bidang dalam formulir tampilan yang ingin Anda biarkan kosong.

Saya menemukan bahwa melakukannya dengan cara ini, dengan mengorbankan beberapa baris kode tambahan, membuat pengujian unit saya menyerupai cara kode dipanggil pada saat runtime lebih dekat membuat mereka lebih berharga. Anda juga dapat menguji apa yang terjadi ketika seseorang memasuki "abc" dalam kontrol terikat ke properti int.


-3
2017-08-13 07:32



Benci untuk necro posting lama, tapi saya pikir saya akan menambahkan pikiran saya sendiri (karena saya hanya punya masalah ini dan berlari di posting ini sambil mencari jawabannya).

  1. Jangan menguji validasi dalam tes pengontrol Anda. Anda dapat memercayai validasi MVC atau menulis sendiri (yaitu tidak menguji kode orang lain, menguji kode Anda)
  2. Jika Anda ingin menguji validasi adalah melakukan apa yang Anda harapkan, uji dalam tes model Anda (saya melakukan ini untuk beberapa validasi regex yang lebih kompleks).

Apa yang benar-benar ingin Anda uji di sini adalah bahwa pengendali Anda melakukan apa yang Anda harapkan untuk dilakukan ketika validasi gagal. Itu kode Anda, dan harapan Anda. Menguji mudah setelah Anda menyadari bahwa itulah yang ingin Anda uji:

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

189
2017-09-28 19:02



Saya mengalami masalah yang sama, dan setelah membaca jawaban dan komentar Paul, saya mencari cara untuk memvalidasi model tampilan secara manual.

saya menemukan tutorial ini yang menjelaskan cara memvalidasi secara manual suatu ViewModel yang menggunakan DataAnnotations. Mereka potongan kode Kunci adalah menjelang akhir posting.

Saya mengubah kode sedikit - dalam tutorial parameter ke-4 dari TryValidateObject dihilangkan (validateAllProperties). Untuk mendapatkan semua anotasi untuk Validasi, ini harus disetel ke true.

Additionaly I merefleksikan kode menjadi metode umum, untuk membuat pengujian validasi valid 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);
        }
    }

Sejauh ini ini telah bekerja sangat baik bagi kami.


83
2017-07-28 13:08



Ketika Anda memanggil metode homeController.Index dalam pengujian Anda, Anda tidak menggunakan salah satu kerangka MVC yang memecat validasi sehingga ModelState.IsValid akan selalu benar. Dalam kode kita, kita memanggil metode Validate helper langsung di controller daripada menggunakan validasi ambient. Saya belum punya banyak pengalaman dengan DataAnnotations (Kami menggunakan NHibernate.Validators) mungkin orang lain dapat menawarkan panduan bagaimana memanggil Validate dari dalam controller Anda.


6
2017-08-13 03:58



Saya sedang meneliti hari ini dan saya menemukan posting blog ini oleh Roberto Hernández (MVP) yang tampaknya memberikan solusi terbaik untuk memecat validator untuk tindakan pengontrol selama pengujian unit. Ini akan menempatkan kesalahan yang benar dalam ModelState ketika memvalidasi entitas.


3
2017-10-05 03:45



Saya menggunakan ModelBinders dalam kasus uji saya untuk dapat memperbarui nilai model.IsValid.

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

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

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

Dengan metode MvcModelBinder.BindModel saya sebagai berikut (pada dasarnya kode yang sama digunakan internal dalam kerangka 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);
        }

2
2018-02-18 19:50



Ini tidak benar-benar menjawab pertanyaan Anda, karena itu meninggalkan DataAnnotations, tetapi saya akan menambahkannya karena mungkin membantu orang lain menulis tes untuk Pengontrol mereka:

Anda memiliki pilihan untuk tidak menggunakan validasi yang disediakan oleh System.ComponentModel.DataAnnotations tetapi masih menggunakan objek ViewData.ModelState, dengan menggunakan AddModelError metode dan beberapa mekanisme validasi lainnya. Misalnya:

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

Ini masih memungkinkan Anda mengambil keuntungan dari Html.ValidationMessageFor() hal yang MVC hasilkan, tanpa menggunakan DataAnnotations. Anda harus memastikan kunci yang Anda gunakan AddModelError cocok dengan apa yang ditunggu oleh tampilan untuk pesan validasi.

Kontroler kemudian menjadi dapat diuji karena validasi terjadi secara eksplisit, daripada dilakukan secara otomatis oleh kerangka MVC.


1
2017-09-23 19:52



Saya setuju bahwa ARM memiliki jawaban terbaik: menguji perilaku pengontrol Anda, bukan validasi bawaan.

Namun, Anda juga dapat menguji unit Model / ViewModel Anda memiliki atribut validasi yang benar yang ditentukan. Katakanlah ViewModel Anda terlihat seperti ini:

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

Tes unit ini akan menguji keberadaan [Required] atribut:

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

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

    Assert.IsNotNull(attribute);
}

1
2018-05-26 16:41