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

他のオプションがどんなものであるかをより詳现に怜蚎したしょう

Source: https://habr.com/ru/post/J176137/


All Articles