因此,我想学习一些TDD,并想看看是否可以测试应该返回一个View的Index动作。

测试未通过,错误是


测试方法
Summumnet.Tests.Controllers.PhysicalTestsControllerTest.IndexShouldReturnView
抛出异常:System.ArgumentException:表达式不是
属性访问:c => c.FindById(1)


这是我的控制器操作代码:

[Authorize]
    [AllowedToEditEHR]
    public class PhysicalTestsController : Controller
    {
        private IUnitOfWork unitOfWork;
        private IRepository<EHR> ehrRepository;
        private const int PAGESIZE = 5;


        public PhysicalTestsController(IUnitOfWork unit)
        {
            unitOfWork = unit;
            ehrRepository = unitOfWork.EHRs;

        }

        public ActionResult Index(int ehrId, int? page)
        {
            EHR ehr = ehrRepository.FindById(ehrId);
            var physicaltests = ehr.PhysicalTests.Where(test => !test.IsDeleted).OrderByDescending(test => test.CreationDate);
            List<PhysicalTestListItem> physicalTestsVM = new List<PhysicalTestListItem>();
            Mapper.Map(physicaltests, physicalTestsVM);
            var paginatedTests = physicalTestsVM.ToPagedList(page ?? 0, PAGESIZE);
            return View(paginatedTests);
        }


这是测试

[TestClass]
    public class PhysicalTestsControllerTest
    {
        [TestMethod]
        public void IndexShouldReturnView()
        {
            // Arrange

            var mock = new Mock<ControllerContext>();
            mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns("nacho");
            mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);

            var mockUnitofWork = new Mock<IUnitOfWork>();
            var mockEhrRepository = new Mock<IRepository<EHR>>();
            mockEhrRepository.SetupGet(c => c.FindById(1)).Returns(new EHR { PhysicalTests = new List<PhysicalTest>()});
            mockUnitofWork.SetupGet(p=>p.EHRs).Returns(mockEhrRepository.Object);
            PhysicalTestsController controller = new PhysicalTestsController(mockUnitofWork.Object);
            controller.ControllerContext = mock.Object;

            // Act
            ViewResult result = controller.Index(1, 0) as ViewResult;

            // Assert
            Assert.IsNotNull(result);
        }

最佳答案

一些提示:


您无需为此测试模拟HttpContext。当然,您的控制器上具有Authorize属性,但是在对控制器进行单元测试时,不会运行动作过滤器。因此,您可以假设用户将通过身份验证和授权。
我相信SetupGet已经过时了。您应该考虑改用Setup

mockUnitofWork.Setup(p => p.EHRs).Returns(mockEhrRepository.Object);


实际上,这就是导致您出错的原因,因为SetupGet只能用于属性,而Setup可以用于几乎任何东西。由于FindById不是属性(它是方法),因此会出现该错误。
设置函数调用时,如果不提供确切的输入参数,而是指定输入参数必须满足的条件,通常会得到更好的结果。当您传递一个类的两个相同但独立的实例并期望它们相同时,这可以避免意外的和通常难以跟踪的测试错误。

在Moq中,可以使用It上的静态方法来执行此操作。如果要允许任何int,只需说It.IsAny<int>()代替整数参数即可。如果要具体说明,可以使用It.Is<int>(i => someCondition(i))使用lambda表达式声明何时应应用该行为。因此,您说的不是.Setup(c => c.FindById(1))

mockEhrRepository.Setup(r => r.FindById(It.Is<int>(i => i == 1)).Returns(...)


对于值类型,这没有什么区别,但是我认为这是一个好习惯。我通常选择不关心给出哪个整数,这稍微简化了设置表达式:

mockEhrRepository.Setup(r => r.FindById(It.IsAny<int>()).Returns(...)



但要明确一点:要消除错误,您需要做的唯一一件事就是将SetupGet更改为Setup

10-07 19:10
查看更多