C# 在模拟存储库中模拟.Single()方法的返回值时出现问题

C# 在模拟存储库中模拟.Single()方法的返回值时出现问题,c#,unit-testing,nunit,asp.net-mvc-5,fakeiteasy,C#,Unit Testing,Nunit,Asp.net Mvc 5,Fakeiteasy,场景:我正在学习如何进行单元测试。目前,我正在与nUnit和Fakeitesy一起进行mvc动作方法的测试。我有一个测试来验证如果传递的id不存在,该方法是否会引发异常。action方法调用.Single()的存储库包装器方法,如果未找到任何内容,该方法将引发异常。这很好 在我的测试中,我执行以下操作: 使用FakeiTasy创建假IRepository 创建测试数据 Configure.Single()包装器方法从我的测试数据中获取数据 问题:我在测试时遇到问题。问题是,当传递一个无效id

场景:我正在学习如何进行单元测试。目前,我正在与nUnit和Fakeitesy一起进行mvc动作方法的测试。我有一个测试来验证如果传递的id不存在,该方法是否会引发异常。action方法调用.Single()的存储库包装器方法,如果未找到任何内容,该方法将引发异常。这很好

在我的测试中,我执行以下操作:

  • 使用FakeiTasy创建假IRepository
  • 创建测试数据
  • Configure.Single()包装器方法从我的测试数据中获取数据
问题:我在测试时遇到问题。问题是,当传递一个无效id时,将在伪存储库的配置代码中而不是在操作方法本身中引发异常原因显而易见。在执行操作方法之前运行配置代码,配置代码对测试数据调用.Single()。。。它(当然是有意的)不包含无效的id。因此它会立即抛出一个异常,甚至不会将其发送到action方法我不确定的是如何解决这个问题。需要在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);
        }
公共视图结果详细信息(int-id)
{
var dbPart=\u存储库
.GetSingleRecord(x=>x.PartID==id);
var viewmodel=new DetailsViewModel()
{
PartID=dbPart.PartID
};
返回视图(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));
        }
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
//创建一个存在的伪PartID
_存在的partID_=1;
//创建一个不存在的伪PartID
不存在的部分=-100;
}
[测试]
public void如果部分不存在,则抛出异常
{
//安排
FakeRepository.FakePartID=不存在的partID;
_fakeRepository=fakeRepository.Create();
_控制器=新零件控制器(_fakeRepository);
//行动与主张
Assert.Throws(()=>
_controller.Details(不存在的partID);
}

伪造存储库代码

        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;
            }
        }
公共类伪造存储库
{
公共静态int?FakePartID{get;set;}
公共静态存储库创建()
{
//创建假存储库
var fakeRepository=A.Fake();
//创建假测试数据
var fakeParts=新列表()
{
新订部分()
{ 
PartID=1,PartDesc=“伪Part 1”
},
新订部分()
{ 
PartID=2,PartDesc=“伪Part 2”
}
};
//配置假存储库以返回假数据
A.CallTo(()=>fakeRepository.GetAllRecords())
.退货(伪造零件);
if(FakePartID.HasValue)
{
/*下面的代码就是问题所在*/
A.CallTo(伪造地址)
.Where(call=>call.Method.Name==“GetSingleRecord”)
.WithReturnType()
.Returns(fakeParts.Single(x=>x.PartID==FakePartID));
}
//返回新创建和配置的fakeRepository
返回伪造地址;
}
}

我想出来了。我需要使用而不是Returns()

returnslazly延迟设置方法的返回值,直到实际调用该方法,而不是在执行方法的配置代码时设置它们

新的工作代码:

A.CallTo(fakeRepository)
 .Where(call => call.Method.Name == "GetSingleRecord")
 .WithReturnType<Part>()
 .ReturnsLazily(() => fakeParts
                       .Single(x => x.PartID == FakePartID));
A.CallTo(fakeRepository)
.Where(call=>call.Method.Name==“GetSingleRecord”)
.WithReturnType()
.随意返回(()=>伪造零件
.Single(x=>x.PartID==FakePartID));