Чистый способ тестирования контроллера ASP.NET MVC без попадания в базу данных? - PullRequest
2 голосов
/ 17 февраля 2010

Я пытаюсь найти хороший способ очистить мои контроллеры, чтобы сделать их более тестируемыми, не полагаясь на постоянное соединение с базой данных. Я думал, что неплохо начал с абстрагирования контекста моего объекта с помощью IObjectContext. Это хорошо работает для контекста, но моя следующая проблема заключается в том, что у меня есть универсальный репозиторий , который я использую в ряде методов действия во всем проекте (см. Код ниже).

В дополнение к конструктору по умолчанию, мой контроллер состоит из перегрузки, которая принимает IObjectContext (простое внедрение зависимости). В моих модульных тестах я могу легко издеваться над IObjectContext. Моя проблема связана с моим общим хранилищем в различных методах действия. Я мог бы добавить несколько дополнительных перегрузок конструктора к контроллеру, но, боюсь, это очень быстро запутается. Однако, если не делать этого, я просто не смог придумать чистый способ улучшить тестируемость, чтобы мне не пришлось полагаться на соединение с базой данных.

Есть ли простое решение, которое я пропускаю?

/// <summary>
/// Initializes a new instance of the HomeController class
/// </summary>
public HomeController(IObjectContext context)
{
    _context = context;
}

/// <summary>
/// GET: /home/index
/// </summary>
/// <returns>Renders the home page</returns>
public ActionResult Index()
{
    List contacts;
    HomeViewModel model;

    using (IRepository<Contact> repository = new DataRepository<Contact>(_context))
    {
        contacts = new List(repository.GetAll());
    }

    model = new HomeViewModel(contacts);

    return View(model);
}

Если мне нужно пойти по пути добавления дополнительных перегрузок конструктора, чтобы учесть мои проблемы, я подумал о добавлении ряда частных свойств (которые могли бы отложить создание репозиториев до тех пор, пока они не потребуются) в мои контроллеры для каждого из репозиториев что методы действия используют. Например:

private IRepository<Contact> _contactRepository;

private IRepository<Contact> ContactRepository
{
    get
    {
        return _contactRepository ?? (_contactRepository = new DataRepository<Contact>());
    }
}

В целях модульного тестирования я смог бы предварительно инициализировать репозитории, используя перегрузки конструктора.

Что вы думаете об этом? Я скучаю по чему-то более чистому, что должно быть очевидно?

Ответы [ 3 ]

5 голосов
/ 17 февраля 2010

Прежде всего, избавьтесь от текущих Bastard Injection перегрузок конструктора. С DI вам нужен только один конструктор, и он принимает все зависимости. (Чтобы включить среду выполнения ASP.NET MVC для создания контроллеров, реализуйте пользовательский IControllerFactory.)

Следующим шагом является внедрение всех ваших зависимостей через конструктор. Когда вы думаете, что это становится грязным из-за слишком большого количества параметров конструктора, это хороший признак того, что вы нарушаете Принцип единой ответственности . Когда это происходит, вы извлекаете Совокупный сервис .

Сполосните и повторите:)

1 голос
/ 26 февраля 2010

Использование вами общего репозитория является скорее средством скрытия зависимостей, чем внедрением зависимостей. Вы должны быть в состоянии увидеть все зависимости, используемые конкретным контроллером: общий репозиторий скрывает этот факт где-то глубоко в недрах ваших контроллеров, что значительно усложняет поддержку (и модульное тестирование) кода. Мое предложение: использовать конкретные хранилища.

Вы также можете взглянуть на материал, управляемый доменом .

1 голос
/ 17 февраля 2010

Ну, я делаю то, что показывает ваш последний пример, чтобы внедрить макеты в мои контроллеры. Он немного пахнет (предназначен для тестируемости), но это неплохое кодирование и отлично подходит для тестирования.

...