Подтвердить что ты не робот

MVC 3: Как узнать, как тестировать NUnit, Ninject и Moq?

Краткая версия моих вопросов:

  • Может ли кто-нибудь указать мне на некоторые хорошие, подробные источники, из которых я может узнать, как реализовать тестирование в моем приложении MVC 3, используя NUnit, Ninject 2 и Moq?
  • Может ли кто-нибудь здесь пояснить, как контроллер-репозиторий развязка, насмешливость и инъекция зависимости работают вместе?

Более длинная версия моих вопросов:

Что я пытаюсь сделать...

В настоящее время я начинаю создавать приложение MVC 3, которое будет использовать Entity Framework 4 с базовым подходом к базе данных. Я хочу сделать это правильно, поэтому я пытаюсь разработать классы, слои и т.д., Чтобы быть высоко проверяемыми. Но у меня мало опыта без модульного тестирования или тестирования интеграции, кроме академического понимания их.

После многих исследований я решил использовать

  • NUnit как платформа для тестирования
  • Ninject 2 как моя инфраструктура внедрения зависимостей
  • Moq как моя издевательская структура.

Я знаю, какая тема лучше всего подходит и т.д., может вступить в это, но на данный момент я действительно не знаю достаточно об этом, чтобы сформировать солидное мнение. Итак, я просто решил пойти с этими бесплатными решениями, которые, кажется, очень понравились и ухожены.

То, что я узнал до сих пор...

Я потратил некоторое время на то, чтобы проработать некоторые из этих материалов, читая такие ресурсы, как:

Из этих ресурсов мне удалось разобраться в необходимости шаблона репозитория, в комплекте с интерфейсами репозитория, чтобы разделить мои контроллеры и логику доступа к данным. Я уже написал некоторые из них в моем приложении, но я признаю, что не знаю, что касается механики всего этого, и я делаю это развязку в поддержку насмешливой или зависимой инъекции или и того, и другого. Таким образом, я, конечно, не прочь услышать от вас, ребята, об этом тоже. Любая ясность, которую я могу получить по этому поводу, поможет мне в этот момент.

Там, где все стало грязно для меня...

Мне казалось, что я хорошо разбираюсь в этом материале, пока не начал обнимать Ninject, как описано в Building Testable ASP.NET MVC Applications, процитированное выше. В частности, я полностью потерялся в момент, когда автор начинает описывать реализацию уровня сервиса, примерно наполовину в документе.

В любом случае, теперь я ищу больше ресурсов для изучения, чтобы попытаться получить различные перспективы вокруг этого материала, пока он не станет иметь для меня смысл.

Подводя итог всему этому, перекинув его на конкретные вопросы, мне интересно следующее:

  • Может ли кто-нибудь указать мне на некоторые хорошие, подробные источники, из которых я может узнать, как реализовать тестирование в моем приложении MVC 3, используя NUnit, Ninject 2 и Moq?
  • Может ли кто-нибудь здесь пояснить, как контроллер-репозиторий развязка, насмешливость и инъекция зависимости работают вместе?

EDIT:

Я только что открыл официальную вики Ninject в Github, поэтому я собираюсь начать с этого, чтобы узнать, начнет ли он прояснять вещи для меня. Но я все еще очень заинтересован в мыслях сообщества SO по всему этому:)

4b9b3361

Ответ 1

Если вы используете пакет Ninject.MVC3 nuget, то часть статьи, которую вы связали, которая вызывает путаницу, не требуется. В этом пакете есть все необходимое, чтобы начать вводить ваши контроллеры, что, вероятно, является самой большой точкой боли.

После установки этого пакета он создаст файл NinjectMVC3.cs в папке App_Start, внутри этого класса будет метод RegisterServices. Здесь вы должны создать привязки между вашими интерфейсами и вашими реализациями.

private static void RegisterServices(IKernel kernel)  
{  
  kernel.Bind<IRepository>().To<MyRepositoryImpl>();
  kernel.Bind<IWebData>().To<MyWebDAtaImpl>();
}        

Теперь в вашем контроллере вы можете использовать инъекцию конструктора.

public class HomeController : Controller {  
    private readonly IRepository _Repo;
    private readonly IWebData _WebData;

    public HomeController(IRepository repo, IWebData webData) {
      _Repo = repo;
      _WebData = webData;
    }
}

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

Это относится не только к контроллеру, но и к любому классу, поэтому в приведенном выше примере, если для реализации репозитория необходим экземпляр WebData для чего-то, вы добавили бы поле readonly и конструктор в свою реализацию репозитория.

Затем, когда дело доходит до тестирования, то, что вы хотите сделать, - это отброшенная версия всех необходимых интерфейсов, так что единственное, что вы тестируете, это код в методе, для которого вы пишете тест. Итак, в моем примере, скажем, что IRepository имеет

bool TryCreateUser(string username);

вызываемый методом контроллера

public ActionResult CreateUser(string username) {
    if (_Repo.TryCreateUser(username))
       return RedirectToAction("CreatedUser");
    else
       return RedirectToAction("Error");
}

То, что вы на самом деле пытаетесь проверить здесь, это то, что оператор if и типы возвращаемых данных вам не нужно создавать реальный репозиторий, который вернет true или false на основе специальных значений, которые вы ему даете. Здесь вы хотите высмеять.

public void TestCreateUserSucceeds() {
    var repo = new Mock<IRepository>();
    repo.Setup(d=> d.TryCreateUser(It.IsAny<string>())).Returns(true);
    var controller = new HomeController(repo);
    var result = controller.CreateUser("test");
    Assert.IsNotNull(result);
    Assert.IsOfType<RedirectToActionResult>(result)
    Assert.AreEqual("CreatedUser", ((RedirectToActionResult)result).RouteData["Action"]);
}

^ Это не скомпилируется для вас, поскольку я знаю, что xUnit лучше, и не помню имена свойств в RedirectToActionResult из верхней части головы.

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

Один из последних советов, характерных для MVC, в любое время, когда вам нужно получить доступ к основным веб-объектам, HttpContext, HttpRequest и т.д., оберните их все за интерфейсом (например, IWebData в моем примере), потому что, пока вы можете издеваться над этими используя * базовые классы, он становится очень болезненным, поскольку у них много внутренних зависимостей, которые вам также нужно высмеять. Также с помощью Moq установите MockBehaviour Strict при создании mocks, и он скажет вам, что что-то называется, что вы не предоставили макет для.

Ответ 2

  • Вот приложение, которое я создаю. Он является открытым исходным кодом и доступен на github и использует все необходимые материалы - MVC3, NUnit, Moq, Ninject - https://github.com/alexanderbeletsky/trackyt.net/tree/master/src

  • Разборка Contoller-Repository проста. Все операции с данными перемещаются в репозиторий. Репозиторий - это реализация некоторого типа IRepository. Контроллер никогда не создает репозитории внутри себя (с оператором new), а скорее принимает их либо аргументом конструктора, либо свойством.

.

public class HomeController {
  public HomeController (IUserRepository users) {

  }
}

Этот метод называется "Инверсия управления". Чтобы поддерживать инверсию управления, вы должны предоставить некоторую инфраструктуру "Injection of Dependency". Ninject - хороший. Внутри Ninject вы связываете определенный интерфейс с классом реализации:

Bind<IUserRepository>().To<UserRepository>();

Вы также замените контроллер по умолчанию factory на свой собственный. Внутри настраиваемого вы делегируете вызов ядру Ninject:

public class TrackyControllerFactory : DefaultControllerFactory
{
    private IKernel _kernel = new StandardKernel(new TrackyServices());

    protected override IController GetControllerInstance(
        System.Web.Routing.RequestContext requestContext,
        Type controllerType)
    {
        if (controllerType == null)
        {
            return null;
        }

        return _kernel.Get(controllerType) as IController;
    }
}

Когда инфраструктура MVC собирается создать новый контроллер, вызов делегируется в пользовательский контроллер factory метод GetControllerInstance, который делегирует его Ninject. Ninject видит, что для создания этого контроллера конструктор имеет один аргумент типа IUserRepository. Используя объявленную привязку, он видит, что "мне нужно создать UserRepository для удовлетворения потребности IUserRepository". Он создает экземпляр и передает его конструктору.

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

Примеры кода:

Ответ 3

Посмотрите: DDD Мельбурн видео - Новый рабочий процесс разработки

Весь процесс разработки ASP.NET MVC 3 был очень хорошо представлен.

Инструменты третьей стороны, которые мне больше всего нравятся:

  • Использование NuGet для установки Ninject для включения DI во всем MVC3 рамки
  • Использование NuGet для установки nSubstite для создания mocks для включения блока тестирование