ASP.NET MVCレッスンE.テスト

レッスンの目的。 コードのテストを作成する方法を学びます。 NUnit。 TDDの適用の原則。 モック。 単体テスト。 統合テスト。 データ生成。



テスト、TDD原則、単体テストなど。


個人的にテストすることは多くの考えのトピックです。 テストは必要ですか? しかし、テストを記述するためにリソースが必要であると主張する人はいません。

次の2つのケースを検討してください。

  1. 私たちはサイトを作成し、顧客に見せ、彼は不正確さと追加の希望のリストを送信し、それらを元気に編集してサイトを顧客に与えます。 彼のサーバーに置きます。 誰も彼のサーバーに行きません、顧客は奇跡が起こらなかったことを理解して、ホスティング/ドメインの支払いを止めます。 サイトは死にかけています。 そこでテストが必要ですか?
  2. サイトを作成し、顧客に見せ、編集リストを送信し、元気に編集してサイトを立ち上げます。 6か月後、サイトには1日あたり300の一意のエントリがあり、この数字は日々増加しています。 顧客は常に新しい機能を要求し、古いコードが成長し始め、時間の経過とともに保守が難しくなります。








ご覧のとおり、ここでのジレンマは、サイトの立ち上げの結果が予測できないことです。 膝の上に作られたサイトが非常に元気に立ち上がったのも私と一緒で、顧客がクールな仕事にお金を払ったが、それを受け入れさえしなかったことが起こりました。 したがって、行動の戦術は次のようになります。





TDDの原理を検討してください。

  1. 課題を読み、失敗するテストを書きます
  2. このテストと他のテストに合格できるコードを作成します
  3. リファクタリング、つまり 必要に応じて重複コードを削除しますが、すべてのテストに合格します




たとえば、次の修正が行われました。



ブログにタグフィールドを追加することにしました。 すでに多くのブログエントリがあるため、このフィールドをオプションにすることにしました。 既存のコードがあるため、足場は使用しませんでした。 手動でレコードの作成を確認しました-すべて問題ありません。 テストを実行します-すべて問題ありません。 しかし、フィールドの変更をUpdatePostに追加するのを忘れていました(cache.Tags = instance.Tags;)。 古いレコードを変更するとき、実際に保存されないタグを追加します。 このテストではバタンと合格しました。 人生は苦痛です!



ご覧のとおり、TDDの基本原則に違反しました。最初に失敗するテストを記述し、次にそれを処理するコードを記述します。 しかし(!)ここには2つ目のトリックがあります-タグ付きのブログエントリの作成をチェックするテストを作成しました。 もちろん、これはすぐにはコンパイルされませんでした(つまり、テストはパスしませんでした)が、ModelViewにthrow New NotImplementedException()のようなものを追加しました。 コンパイルされたものはすべて、テストは赤で点灯します。このフィールドにタグを追加し、例外を削除して、テストに合格します。 他のすべてのテストも合格します。 原則は尊重されますが、エラーは残ります。



すべての原則について、それが機能しない状況があります。 つまり そのようなことはありません-彼らは脳をオフにし、運転しました。 1つ確かなことは、これがこれらの考慮事項からの主な結論です。

テストは迅速に書かれるべきです

そのため、主にサイトでどのようなタスクを解決しますか:





これらが主なアクションです。 たとえば、登録方法は次のとおりです。





これらすべての単体テストを作成しましょう。



それに取り掛かろう。



NUnitをインストールする


リンクhttp://sourceforge.net/projects/nunit/に従って、NUnitをインストールします。 NUnit Test AdapterもVSにインストールします(VSでテストを直接実行するため)。





タイプSolution Folder Testのフォルダーを作成し、それにLessonProject.UnitTestプロジェクトを追加して、そこにNUnitをインストールします。

Install-Package NUnit
      
      







(/Test/Default/UserContoller.cs)にUserControllerTestクラスを作成します。

  [TestFixture] public class UserControllerTest { }
      
      







そのため、テストメソッドMethod_Scenario_ExpectedBehaviorの名前を記述する原則:





たとえば、登録のためにUserViewクラスでViewを最初に返すことを確認します。

  public void Register_GetView_ItsOkViewModelIsUserView() { Console.WriteLine("=====INIT======"); var controller = new UserController(); Console.WriteLine("======ACT======"); var result = controller.Register(); Console.WriteLine("====ASSERT====="); Assert.IsInstanceOf<ViewResult>(result); Assert.IsInstanceOf<UserView>(((ViewResult)result).Model); }
      
      







したがって、すべてのテストは3つの部分に分かれていますInit-> Act-> Assert:





[テストエクスプローラー]タブを開きます。





NUnitアダプターが正しくインストールされていれば、テストメソッドが表示されます。

始めます。 テストに合格すると、シャンパンを開けることができます。 うんこ。 これは最も簡単な部分にすぎませんが、何かを保存する部分についてはどうでしょう。 この場合、データベースはありません。リポジトリはnull、ゼロ、何もありません。

次に、初期化(ドキュメント)のクラスとメソッドを学習します。 SetUpFixture-この属性でマークされたクラスは、テスト前に初期化し、テスト後にクリーンアップするメソッドがあることを意味します。 これは同じ名前空間に適用されます。





クラスUnitTestSetupFixture.cs(/Setup/UnitTestSetupFixture.cs)を作成します。

  [SetUpFixture] public class UnitTestSetupFixture { [SetUp] public void Setup() { Console.WriteLine("==============="); Console.WriteLine("=====START====="); Console.WriteLine("==============="); } [TearDown] public void TearDown() { Console.WriteLine("==============="); Console.WriteLine("=====BYE!======"); Console.WriteLine("==============="); } }
      
      







実行して取得:

===============

=====START=====

===============

=====INIT======

======ACT======

====ASSERT=====



===============

=====BYE!======

===============









モック


そのため、Mockはパロディオブジェクトです。 つまり たとえば、データベースではなく、データベースに似たものです。 ミラージュ、一般的に。 スタブもあります-これはスタブです。 スタブメソッドの例:

 public int GetRandom() { return 4; }
      
      







ただし、Mockを使用します。

 Install-Package Moq
      
      







使用する環境を定義して、Mockオブジェクトを初期化します。 基本的に、これがかつてNinject Kernelにもたらしたすべてです。





そして、私は小さな発言をします。 ConfigをMirageオブジェクトにレンダリングすることはできません。 これが完全に不可能であるという計画ではなく、計画で-これは悪い仕事です。 たとえば、string.Format()がFormatExceptionエラーをスローするようにレターテンプレートを変更しました。 そして、テストでは、すべてが正常であり、テストは正常に合格します。 そして、彼はその後何に責任がありますか? まさか。 したがって、構成ファイルは元のまま使用する必要があります。 あとで残しておきましょう。



IMapperに関しては、これは必要ありません。CommonMapperを安全に使用できます。

ただし、最初に、テストモードで動作するようにIKernelを初期化します。 App_Start / NinjectWebCommon.csでは、RegisterServicesメソッドで、インターフェイスの実装方法を指定し、bootstrapper.Initialize(CreateKernel)で呼び出します。 将来的には、DependencyResolver.GetService()を介してサービスを受け取ることになります。 したがって、NinjectDependencyResolver(/Tools/NinjectDependencyResolver.cs)を作成します。

 public class NinjectDependencyResolver : IDependencyResolver { private readonly IKernel _kernel; public NinjectDependencyResolver(IKernel kernel) { _kernel = kernel; } public object GetService(Type serviceType) { return _kernel.TryGet(serviceType); } public IEnumerable<object> GetServices(Type serviceType) { try { return _kernel.GetAll(serviceType); } catch (Exception) { return new List<object>(); } } }
      
      







SetUp(/Setup/UnitTestSetupFixture.cs)にメソッドを追加します。

 [SetUp] public virtual void Setup() { InitKernel(); } protected virtual IKernel InitKernel() { var kernel = new StandardKernel(); DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel)); InitRepository(kernel); //  return kernel; }
      
      







MockRepositoryを作成する

(/Mock/Repository/MockRepository.cs):

 public partial class MockRepository : Mock<IRepository> { public MockRepository(MockBehavior mockBehavior = MockBehavior.Strict) : base(mockBehavior) { GenerateRoles(); GenerateLanguages(); GenerateUsers(); } }
      
      





(/Mock/Repository/Entity/Language.cs)

 namespace LessonProject.UnitTest.Mock { public partial class MockRepository { public List<Language> Languages { get; set; } public void GenerateLanguages() { Languages = new List<Language>(); Languages.Add(new Language() { ID = 1, Code = "en", Name = "English" }); Languages.Add(new Language() { ID = 2, Code = "ru", Name = "" }); this.Setup(p => p.Languages).Returns(Languages.AsQueryable()); } } }
      
      







(/Mock/Repository/Entity/Role.cs)

  public partial class MockRepository { public List<Role> Roles { get; set; } public void GenerateRoles() { Roles = new List<Role>(); Roles.Add(new Role() { ID = 1, Code = "admin", Name = "Administrator" }); this.Setup(p => p.Roles).Returns(Roles.AsQueryable()); } }
      
      







(/Mock/Repository/Entity/User.cs)



 public partial class MockRepository { public List<User> Users { get; set; } public void GenerateUsers() { Users = new List<User>(); var admin = new User() { ID = 1, ActivatedDate = DateTime.Now, ActivatedLink = "", Email = "admin", FirstName = "", LastName = "", Password = "password", LastVisitDate = DateTime.Now, }; var role = Roles.First(p => p.Code == "admin"); var userRole = new UserRole() { User = admin, UserID = admin.ID, Role = role, RoleID = role.ID }; admin.UserRoles = new EntitySet<UserRole>() { userRole }; Users.Add(admin); Users.Add(new User() { ID = 2, ActivatedDate = DateTime.Now, ActivatedLink = "", Email = "chernikov@gmail.com", FirstName = "Andrey", LastName = "Chernikov", Password = "password2", LastVisitDate = DateTime.Now }); this.Setup(p => p.Users).Returns(Users.AsQueryable()); this.Setup(p => p.GetUser(It.IsAny<string>())).Returns((string email) => Users.FirstOrDefault(p => string.Compare(p.Email, email, 0) == 0)); this.Setup(p => p.Login(It.IsAny<string>(), It.IsAny<string>())).Returns((string email, string password) => Users.FirstOrDefault(p => string.Compare(p.Email, email, 0) == 0)); } }
      
      







Mockの仕組みを見てみましょう。 彼はセットアップなどの良い方法を持っています(再び?!しっかりしたセットアップ!)、これは次のように動作します:

this.Setup( ).Returns( );







例:

this.Setup(p => p.WillYou()).Returns(true);







他のオプションがどんなものであるかをより詳細に検討しましょう:




All Articles