1

シナリオ:単体テストの方法を学んでいます。現在、nUnit と FakeItEasy を使用した mvc アクション メソッドのテストに取り組んでいます。存在しない ID が渡された場合にメソッドが例外をスローすることを確認するテストがあります。アクション メソッドは、何も見つからない場合に例外をスローする .Single() のリポジトリ ラッパー メソッドを呼び出します。これはいい。

私のテストでは、次のことを行います。

  • FakeItEasy を使用して偽の IRepository を作成する
  • テストデータの作成
  • テスト データからデータを取得するように .Single() ラッパー メソッドを構成する

問題:これをテストする際に問題があります。問題は、無効な ID が渡されると、アクション メソッド自体ではなく、偽のリポジトリの構成コードで例外がスローされることです。その理由は明らかです。アクションメソッドが実行される前に構成コードが実行され、構成コードはテストデータで .Single() を呼び出します...これには(もちろん意図的に)無効なIDが含まれていません。そのため、その場で例外をスローし、アクション メソッドに到達することさえありません。私がよくわからないのは、これを回避する方法です。アクション メソッド内で例外をスローする必要があります。この難問を回避する方法で戻り値を構成する方法がわかりません。

コード:
コントローラ コード

        public ViewResult Details(int id)
        {
            var dbPart = _repository
                             .GetSingleRecord<Part>(x => x.PartID == id);

            var viewmodel = new DetailsViewModel()
            {
                PartID = dbPart.PartID
            };

            return View(viewmodel);
        }


テストコード

        [TestFixtureSetUp]
        public void TestFixtureSetUp()
        {
            // Create a fake PartID that exists
            partID_that_exists = 1;

            // Create a fake PartID that doesn't exist
            partID_that_doesnt_exist = -100;
        }

        [Test]
        public void an_exception_is_thrown_if_the_part_doesnt_exist()
        {
            // Arrange
            FakeRepository.FakePartID = partID_that_doesnt_exist;
            _fakeRepository = FakeRepository.Create();
            _controller = new PartController(_fakeRepository);

            // Act & Assert
            Assert.Throws<InvalidOperationException>(() => 
                         _controller.Details(partID_that_doesnt_exist));
        }


偽のリポジトリ コード

        public class FakeRepository
        {
            public static int? FakePartID { get; set; }              

            public static IBasicRepository Create()
            {
                // Create fake repository
                var fakeRepository = A.Fake<IBasicRepository>();

                // Create fake test data
                var fakeParts = new List<Part>()
                {
                    new Part() 
                    { 
                        PartID = 1, PartDesc = "Fake Part 1"
                    },
                    new Part() 
                    { 
                        PartID = 2, PartDesc = "Fake Part 2"
                    }
                };

                // Configure fake repository to return fake data
                A.CallTo(() => fakeRepository.GetAllRecords<Part>())
                                             .Returns(fakeParts);
                if (FakePartID.HasValue)
                {
                    /* BELOW CODE IS THE PROBLEM */
                    A.CallTo(fakeRepository)
                   .Where(call => call.Method.Name == "GetSingleRecord")
                   .WithReturnType<Part>()
                   .Returns(fakeParts.Single(x => x.PartID == FakePartID));
                }

                // Return the newly created & configured fakeRepository
                return fakeRepository;
            }
        }
4

1 に答える 1

1

私はそれを考え出した。Returns() の代わりにReturnsLazily()を使用する必要がありました。

ReturnsLazilyは、メソッドの構成コードが実行されたときに設定するのではなく、メソッドが実際に呼び出されるまでメソッドの戻り値の設定を遅らせます。

新しい、実用的なコード:

A.CallTo(fakeRepository)
 .Where(call => call.Method.Name == "GetSingleRecord")
 .WithReturnType<Part>()
 .ReturnsLazily(() => fakeParts
                       .Single(x => x.PartID == FakePartID));
于 2014-04-22T21:44:08.567 に答える