ユニット試験MVC検証
-
13-09-2019 - |
質問
したいので試験が私のコントローラのアクションの補正誤差のModelState妥当性を検査するときに企業がを使用していDataAnnotation検証MVC2 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);
}
ちょうどあなたが空のままにしておきたいことを形成し、あなたのテストは、ビュー内のすべてのフィールドにNULL値を追加してください。
私は、コードのいくつかの余分な行を犠牲にして、このようにそれをやって、私のユニットテストはコードがより密接にそれらをより価値のある作り、実行時に呼び出される方法に似ていますことを見出しました。また、誰かがint型のプロパティにバインドされたコントロールで「ABC」を入力したときに何が起こるかをテストすることができます。
他のヒント
嫌いな刀をする必要がありますが、ようと思って自分で考えらったこのような問題を走りがここにポストを追求する一方で、その答え).
- な試験検証コントローラ。また信託MVCの検証や自分の(テストしていないその他のコード、テストコード)
- だいたい試験検証していますが、ご了承ください、試験での模型実験(I doこのカップルの複雑な正規表現検証).
だか試したいと思いここには、あなたのコントローラはいますが、ご了承くださいすることができるようになりますが検証に失敗します。このコードは、ご期待ください。試験では簡単だがそのすべて試したいと思い:
[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));
}
私は同じ問題を抱えていた、とポールの答えとコメントを読んだ後、私は手動でビューモデルを検証する方法を探しています。
私は<のhref = "http://johan.driessen.se/archive/2009/11/18/testing-dataannotation-based-validation-in-asp.net-mvc.aspx" のrel = "noreferrerました「>手動DataAnnotationsを使用するのViewModelを検証する方法を説明し、このチュートリアルに。これらのキーコードスニペットは、記事の最後の方である。
私はわずかにコードを修正 - チュートリアルでTryValidateObjectの4番目のパラメータが省略された(validateAllProperties)。すべての注釈を検証してもらうためには、これをtrueに設定する必要があります。
Additionaly、私は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メソッドを呼び出すと、あなたはModelState.IsValidは常にtrueになりますので、検証をオフ発射MVCフレームワークのいずれかを使用していません。我々のコードでは、コントローラに直接ヘルパーValidateメソッドを呼び出すのではなく、周囲の検証を使用しました。私は多分誰か他の人がどのようにあなたのコントローラ内から検証を呼び出すためのガイダンスを提供することができます(私たちはNHibernate.Validatorsを使用)DataAnnotationsで多くの経験を持っていなかった。
私は今日、これを研究していたと私は<のhref =「http://blog.overridethis.com/blog/post/2010/07/08/MVC2-Validation-and-Testing-e28093-Refactored.aspx」が見つかりrel =「nofollowを」>ユニットテスト中にコントローラのアクションのためのバリデータを発射するための最良のソリューションを提供しているようだロベルト・ヘルナンデス(MVP)によってにこのブログの記事。エンティティの検証時にこれが正しいにModelStateにエラーを入れます。
私はmodel.IsValid値を更新することができるように私のテストケースでModelBindersを使用しています。
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);
}
それはDataAnnotationsを放棄ので、これはまさに、あなたの質問に答えていないが、それは他の人が自分のコントローラのテストを書くに役立つかもしれないので、私はそれを追加します。
あなたはそのAddModelError
方法や他のいくつかの検証メカニズムを使用することにより、System.ComponentModel.DataAnnotationsが提供する検証を使用したが、それでもViewData.ModelStateオブジェクトを使用していないオプションがあります。例えばます:
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
で使用キーがビューの検証メッセージのために期待しているものと一致を確認する必要があります。
コントローラは、次に、検証可能となる。
私は、ARMが最良の答えがあることに同意します:あなたのコントローラの動作をテストし、ない組み込みのバリデーション
しかし、あなたはあなたのモデル/ビューモデルが定義された正しい検証属性を持っても、ユニットテストすることができます。さんがあなたの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);
}
ARMとは対照的に、私は墓掘りに問題はありません。だからここに私の提案です。これは、ジャイルズ・スミスと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.");
}
この設定の利点は、私はすべての私のモデルのテストのためのテストコントローラを再利用することができますし、コントローラについてもう少しモックまたはコントローラが持つ保護されたメソッドを使用するためにそれを拡張することができるかもしれないということです。
それがお役に立てば幸いです。
あなたが検証を気しかし、あなたは唯一の抽象化の最高レベルで、あなたのアクションメソッドのバリデーションを心配している場合、あなたは、それがどのように実装されるか気にしないのであれば、それはさえDataAnnotations、ModelBindersかを使用して実装されているかどうかに関係なく、次のようにActionFilterAttributes、あなたはXania.AspNet.Simulator nugetパッケージを使用することができます:
install-package Xania.AspNet.Simulator
-
var action = new BlogController()
.Action(c => c.Index(new BlogPost()), "POST");
var modelState = action.ValidateRequest();
modelState.IsValid.Should().BeFalse();
のWeb 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);
}
}