不能模拟ASP中的方法调用.. NET MVC控制器使用Moq

本文关键字:控制器 MVC Moq NET 调用 ASP 模拟 方法 不能 | 更新日期: 2023-09-27 18:01:24

我有以下代码在"UserController"在我的ASP。. NET MVC应用程序:

public class UserController : Controller
{
    public ActionResult Index()
    {
        return RedirectToAction("List");
    }
    public ActionResult List()
    {
        IUserRepository repo = new UserRepository();
        IQueryable<Business.Entities.User> users = repo.GetAll();
        return View("List", users);
    }
}

使用Moq,我想模拟数据库调用"repo.GetAll()"。以下是我的测试内容:

[Test]
public void List()
{
    Mock<IUserRepository> mockRepo = new Mock<IUserRepository>();
    mockRepo.Setup(ur => ur.GetAll()).Returns(MockedGetAll());
    var v = mockRepo.Object.GetAll();
    var controller = new UserController();
    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);
}

我还有一个函数,它将返回一些静态数据来满足测试:

private IQueryable<User> MockedGetAll()
{
    List<User> users = new List<User>();
    users.Add(new User(1, "mark.paterson", "mark.paterson@yahoo.com", "Mark", "Paterson", false, true));
    users.Add(new User(2, "nikki.paterson", "nikki.paterson@yahoo.com", "Nikki", "Paterson", false, true));
    return users.AsQueryable();
}

测试在"Assert.Greater"处中断。我得到0条记录,而不是2条。当我调试代码时,代码实际上返回数据库调用的结果,它应该是0条记录,而不是模拟数据。

不能模拟ASP中的方法调用.. NET MVC控制器使用Moq

下面这行删除了所有内容,并引入了一个不可能独立进行单元测试的控制器和数据访问层之间的强耦合:

IUserRepository repo = new UserRepository();
绝对不要在任何应用程序(不仅仅是ASP)中写这样的东西。净MVC)。不管你怎么做,如果你写这样的代码,它总是会在你的单元测试中中断,你将无法测试它。

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

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

public class UserController : Controller
{
    private readonly IUserRepository _repo;
    public UserController(IUserRepository repo)
    {
        _repo = repo;
    }
    public ActionResult Index()
    {
        return RedirectToAction("List");
    }
    public ActionResult List()
    {
        IQueryable<Business.Entities.User> users = _repo.GetAll();
        return View("List", users);
    }
}

现在可以通过mock进行单元测试:

[Test]
public void List()
{
    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框架的人通常会这样写代码,并提供两个构造函数(一个用于单元测试,另一个用于实际应用):

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

我提供这个是为了说明你绝对不应该做的另一件事,并强调这是穷人的DI

hackack也在他的博客文章中讨论了这些问题。