2016-09-18 7 views
1

Я изучаю модульное тестирование и Moq для ASP.NET MVC 5. Я пытаюсь написать свой первый модульный тест для действия индекса одного из моих контроллеров.Как я могу использовать Moq для проверки моего действия индекса, которое возвращает список из базы данных?

Вот код для действия индекса.

[Authorize] 
public class ExpenseController : Controller 
{ 
    private ApplicationDbContext db = new ApplicationDbContext(); 

    // GET: /Expense/ 
    public ActionResult Index() 
    { 
     return View(db.Expenses.ToList().Where(m => m.ApplicationUserId == User.Identity.GetUserId())); 
    } 
} 

Все, что я хочу сделать, это просто проверить, что возвращаемый вид не является нулевым

Что-то вроде этого

[TestMethod] 
    public void ExpenseIndex() 
    { 
     // Arrange 
     ExpenseController controller = new ExpenseController(); 

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

     // Assert 
     Assert.IsNotNull(result); 
    } 

Конечно, это не работает из-за подключения к базе данных и использование ApplicationUserId, так что бы вы, ребята, помогли мне выполнить moq и unit проверить это действие или порекомендовать мне учебник, в котором я могу ознакомиться с насмешкой в ​​ASP.NET MVC.

+0

Рассказать о зависимости от базы данных/dbcontext, а затем вы можете высмеять это в контроллере – Nkosi

+0

@Nkosi Не могли бы вы предоставить мне больше информации. Спасибо. – Baso

+0

Ваш контроллер имеет жесткую зависимость от 'ApplicationDbContext', что затрудняет издевательство над вашим модульным тестом. Вам понадобится реорганизовать ваш dbcontext, чтобы наследовать от абстракции, которая раскрывает функциональность dbcontext, и тогда у вас есть контроллер, зависящий от этой абстракции, а не от конкреции. оттуда вы можете использовать инфраструктуру DI, чтобы внедрить зависимость в свой контроллер, и теперь у вас также есть возможность заменить абстракцию макетной реализацией в ваших тестах, если необходимо, – Nkosi

ответ

1

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

public class ExpenseController : Controller 
    { 
     private ApplicationDbContext db = new ApplicationDbContext(); 

     // GET: /Expense/ 
     public ActionResult Index() 
     { 
      return View(GetUserExpenses()); 
     } 

     protected virtual List<Expense> GetUserExpenses() 
     { 
      return db.Expenses.ToList().Where(m => m.ApplicationUserId == User.Identity.GetUserId()); 
     } 
    } 

Тогда , создайте класс-заглушку, полученный из вашего контроллера, и переопределите метод GetUserExpenses(). Он должен выглядеть следующим образом:

public class ExpenseControllerStub : ExpenseController 
    { 
     protected override List<Expense> GetUserExpenses() 
     { 
      return new List<Expense>(); 
     } 
    } 

Сейчас в тестовом модуле, создать экземпляр из ExpenseControllerStub не из ExpenseController, и он должен работать:

[TestMethod] 
    public void ExpenseIndex() 
    { 
     // Arrange 
     ExpenseControllerStub controller = new ExpenseControllerStub(); 

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

     // Assert 
     Assert.IsNotNull(result); 
    } 

Это, как сделать это вручную. Если вам нужно использовать насмешливые рамки для этого, вам нужно будет сделать GetUserExpenses() общественность не защищена, а затем сделать установку для возврата пустого списка расходов, что-то вроде:

var mock = new Moq.Mock<ExpenseController>(); 
mock.Setup(m => m.GetUserExpenses().Returns(new List<Expense>()); 

Но я не предпочитаю сделать этот метод общедоступным! может быть, есть способ Moq для настройки/настройки защищенных методов, но я не знаю об этом.

Редактировать: Лучшее решение - полностью абстрагировать репозиторий Expenses, в этом случае насмехается над ним.

Другое решение заключается в том, чтобы вставить DbContext в конструктор контроллера и использовать фальшивую фреймворк, чтобы издеваться над ним и DbSet Expenses. Вы можете найти образец для этого here

Редактировать # 2: Вы также можете использовать TestStack.FluentMVCTesting или MvcContrib.TestHelper, чтобы облегчить вам тестирование MVC.

+0

Благодарим вас за ответ, хотя он решит текущую проблему, но это не очень практичное решение. В настоящее время я читаю о том, как абстрагировать репозиторий, как вы упомянули в конце, если бы вы могли с этим помочь, я был бы так благодарен :) – Baso

+1

Нет, это очень практичное решение, особенно когда вы работаете с устаревшим кодом. Это может быть простейшая из немногих вещей, которые вы можете сделать тогда. Реализация хранилища имеет множество вариаций - в зависимости от ваших потребностей - и EF уже реализует общий репозиторий для вас (DbSet). Во всяком случае, я обновил свой ответ с помощью внешней ссылки о том, как издеваться над DbContext и DbSet –