【问题标题】:How do i mock a another method which is called in controller action?我如何模拟在控制器操作中调用的另一种方法?
【发布时间】:2018-09-03 07:17:02
【问题描述】:

这里我有一个场景,我必须在我的控制器操作中调用另一个函数来实现某些输入目的。 如下。

public IActionResult Dashboard()
        {
            try
            {
                    var allStuds = repository.GetAllStudentss();
                    var result = getFirstClassResult(allStuds);
                    return View("Dashboard", result );                         
            }
            catch (Exception ex)
            {             
                return View();
            }
        }


 public List<DashboardViewModel> getFirstClassResult(List<DashboardViewModel> model)
        {
      return students.OrderByDescending(x=>x.ExamDate).ToList();
    }

这是我的 tets 盒子里的代码。

var controller = new StudentController(repositoryMock.Object, null, sessionManagerMock.Object);
    var date = new DateTime();
    var model= new List<DashboardViewModel>          
    controller.ControllerContext.HttpContext = GetHttpContext(CreateSessionMock(true), authenticationServiceMock);
    controller.TempData = new TempDataDictionary(controller.ControllerContext.HttpContext, Mock.Of<ITempDataProvider>());
    controller.Url = GetUrlHelper();
    contextMock.Setup(mock => mock.GetAllStudents()).Returns(model);
    var repository = new StudentRepository(factoryMock.Object);
    var res = repository.GetAllStudents();                
    var result = controller.Dashboard() as ViewResult;
    Assert.Equal("Dashboard", result.ViewName);

在这里我正在编写测试用例,我必须在其中模拟getFirstClassResult(allStuds) 方法。 这是我做不到的。

【问题讨论】:

  • 通常你不能模拟一个非虚拟成员,因为模拟只是创建一个从你模拟的类型派生的类型。但是它应该如何为您无法覆盖的东西创建覆盖?这是不可能的。除此之外,“我不能这样做”不是一个正确的问题描述。请详细说明您尝试过的具体内容以及得到的结果。
  • 此外,您通常不应该模拟被测系统,即您要测试的类。
  • 好的,但是如果我想为上述场景编写测试用例呢?
  • 好吧,我看不到太多场景。但是,您似乎不想模拟您的 getFirstClassResult-method,而是模拟数据库层,这意味着您的情况下的 repository.GetAllStudents-method。为什么不简单地嘲笑这个 inetad 只返回几个学生呢?

标签: c# unit-testing tdd


【解决方案1】:

下面这行将扼杀一切,并在控制器和数据访问层之间引入了一个不可能独立的单元测试强耦合:

IUserRepository repo = new UserRepository();

绝对不要在任何应用程序中编写这样的东西(不仅是 ASP.NET MVC)。如果您编写这样的代码,它总是会在您的单元测试中中断,您将无法对其进行测试。

这是不可能模拟/单元测试的。

您应该使用构造函数注入来削弱层之间的耦合:

public class UserController : Controller{
private readonly IUserRepository _repos;
public UserController(IUserRepository repo)
{
    _repos = repo;
}

public ActionResult Index()
{
    return RedirectToAction("Details");
}

public ActionResult Details()
{
    IQueryable<Business.Entities.User> users = _repos.GetAll();
    return View("Details", users);
}}

现在您可以通过模拟来单独进行单元测试:

   [Test]
   public void Details()
   {
Mock<IUserRepository> mockRepo = new Mock<IUserRepository>();
mockRepo.Setup(ur => ur.GetAll()).Returns(MockedGetAll());
var v = mockRepo.Object.GetAll();

var controller = new UserController(mockRepo.Object);
var result = controller.List() as ViewResult;
var model = result.ViewData.Model as IQueryable<User>;

Assert.AreEqual("List", result.ViewName);
Assert.IsNotNull(model);
Assert.Greater(model.Count(), 0);}

显然,因为现在您的控制器依赖于这个存储库,您可以使用 DI 框架来配置您的依赖项。

不想使用 DI 框架的人经常会这样写代码,并提供 2 个构造函数(一个用于单元测试,一个用于实际应用):

private readonly IUserRepository _repo;
public UserController(IUserRepository repo)
{
_repo = repo;
 }
 public UserController(): this(new UserRepository())
 {}

另请参阅 religion of dependency injection

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-14
    • 1970-01-01
    相关资源
    最近更新 更多