【问题标题】:Trouble mocking return value of .Single() method in mocked repository在模拟存储库中模拟 .Single() 方法的返回值时遇到问题
【发布时间】:2014-04-23 22:02:01
【问题描述】:

场景:我正在学习如何进行单元测试。目前正在使用 nUnit 和 FakeItEasy 对 mvc 操作方法进行测试。如果传递了一个不存在的 id,我有一个测试来验证该方法是否会引发异常。 action 方法调用 .Single() 的存储库包装器方法,如果没有找到,它将抛出异常。这很好。

在我的测试中,我做了以下事情:

  • 使用 FakeItEasy 创建假 IRepository
  • 创建测试数据
  • 配置 .Single() 包装方法以从我的测试数据中获取数据

问题:我在测试这个时遇到了问题。问题是,当传递一个无效的 id 时,会在假存储库的配置代码中引发异常,而不是在操作方法本身中引发异常。 原因很明显。 配置代码在执行操作方法之前运行,配置代码在测试数据上调用 .Single() ......(当然是故意的)不包含无效的 ID。所以它会立即抛出一个异常,甚至永远不会进入 action 方法。 我不确定的是如何解决这个问题。需要在操作方法中抛出异常。我不知道如何以避免这个难题的方式配置返回值。

代码:
控制器代码

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

【问题讨论】:

    标签: c# unit-testing nunit asp.net-mvc-5 fakeiteasy


    【解决方案1】:

    我想通了。我需要使用ReturnsLazily() 而不是 Returns()。

    ReturnsLazily 延迟设置方法的返回值,直到该方法被真正调用,而不是在方法的配置代码时设置它们被执行。

    新的工作代码:

    A.CallTo(fakeRepository)
     .Where(call => call.Method.Name == "GetSingleRecord")
     .WithReturnType<Part>()
     .ReturnsLazily(() => fakeParts
                           .Single(x => x.PartID == FakePartID));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-08
      • 1970-01-01
      • 2011-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多