所以我想学习一点TDD,想看看我是否可以测试一个应该只返回一个View的Index动作。
测试没有通过,错误是
测试方法 Summumnet.Tests.Controllers.PhysicalTestsControllerTest.IndexShouldReturnView 抛出异常:System.ArgumentException:Expression不是 物业访问: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);
}
答案 0 :(得分:8)
一些指示:
您无需为此测试模拟HttpContext
。当然,您的控制器上有Authorize
属性,但是当您对控制器进行单元测试时,不会运行操作过滤器。因此,您可以假设用户将经过身份验证和授权。
SetupGet
已经过时了。您应该考虑使用Setup
代替:
mockUnitofWork.Setup(p => p.EHRs).Returns(mockEhrRepository.Object);
这实际上是导致错误的原因,因为SetupGet
只能用于属性,而Setup
可以用于或多或少的任何事情。由于FindById
不是属性(它是一种方法),因此您会收到该错误。
设置函数调用时,如果不提供确切的输入参数,通常会得到更好的结果,而是指定输入参数必须满足的条件。当你传入一个类的两个完全相同但又相互独立的实例时,这可以避免意外且经常难以跟踪的测试错误,并期望它们是相同的。
在Moq中,您可以使用It
上的静态方法来执行此操作。如果您想允许任何int
,只需说It.IsAny<int>()
代替整数参数。如果您想要具体,可以使用lambda表达式使用It.Is<int>(i => someCondition(i))
声明何时应该应用该行为。所以,而不是.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
< /强>