Не удается смоделировать вызов метода в контроллере ASP.NET MVC с помощью Moq - PullRequest
5 голосов
/ 07 июля 2011

У меня есть следующий код в "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".Большой».Я получаю 0 записей вместо 2. Когда я отлаживаю код, код фактически возвращает результат вызова базы данных, который должен быть 0 записей, а не проверенные данные.

1 Ответ

15 голосов
/ 07 июля 2011

Следующая строка убивает все и представляет невозможное для модульного тестирования изолированное прочное соединение между вашим контроллером и уровнем доступа к данным:

IUserRepository repo = new UserRepository();

Абсолютно никогда не пишите ничего подобного в любом приложении (не только в ASP)..NET 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);
    }
}

Теперь вы можете проводить модульное тестирование изолированно, издеваясь:

[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-фреймворк, часто пишут такой код и предоставляют 2 конструктора (один для модульного тестирования и один для реального приложения):

private readonly IUserRepository _repo;
public UserController(IUserRepository repo)
{
    _repo = repo;
}

public UserController(): this(new UserRepository())
{

}

Я предоставляю это для того, чтобы проиллюстрировать еще одну вещь, которую вы абсолютно никогда не должны делать, и подчеркнуть тот факт, что это DI для бедного человека .

Взломанный также обсуждал эти проблемы в своем блоге .

...