Если вы используете пакет nuget Ninject.MVC3 , то часть статьи, на которую вы ссылались, которая вызывала путаницу, не понадобится.В этом пакете есть все, что вам нужно, чтобы начать внедрять свои контроллеры, что, вероятно, является самой большой проблемой.
После установки этого пакета он создаст файл 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 в моем примере), потому что, хотя вы можете имитировать их, используя * Base классы, это становится очень болезненным, так как они имеют много внутренних зависимостей, вам также необходимоmock.
Также с Moq, установите MockBehaviour на Strict при создании макетов, и он сообщит вам, если что-то вызывается, для которого вы не предоставили макет.