Использование Ninject.MockingKernel с Asp.Net Web API
Я настроил проект Web API с использованием Ninject, и я использовал исправление, подробно описанное здесь, чтобы заставить его работать с последней версией Web API. Все работает нормально, но я сейчас пытаюсь написать несколько тестов.
Я использую хостинг в памяти для запуска проекта для тестов, как подробно описано здесь, так как у меня есть DelegatingHandler
который выполняет аутентификацию, а затем устанавливает свойство в сообщении запроса, которое используется всеми контроллерами Api.
Итак, у меня есть базовый класс для моих тестов, и у меня есть SetUp
метод, в котором я настроил HttpServer
и конфигурация, которую я в значительной степени взял из моего рабочего кода Ninject:
[SetUp]
public void Setup()
{
bootstrapper = new Bootstrapper();
DynamicModuleUtility.RegisterModule(
typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(
typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("Login",
"api/auth/token",
new { controller = "Users", action = "Login" });
config.IncludeErrorDetailPolicy =
IncludeErrorDetailPolicy.Always;
config.DependencyResolver =
new NinjectResolver(CreateKernel());
config.MessageHandlers.Add(
new AuthenticationHandler(CreateUserManager()));
Server = new HttpServer(config);
}
Вот как я создаю MoqMockingKernel
:
private static IKernel CreateKernel()
{
var kernel = new MoqMockingKernel();
kernel.Bind<Func<IKernel>>()
.ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>()
.To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver =
new NinjectResolver(kernel);
return kernel;
}
И вот как я регистрирую объекты для использования:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUserManager>().ToMock();
kernel.Bind<UsersController>().ToSelf();
}
Хотя я не тестирую контроллер как таковой, я хочу, чтобы вызывался соответствующий экземпляр, поэтому я привязываю его к ToSelf. Я должен признать, что я предполагаю, что это правильно. Это пример теста:
public void UserCannotLogin()
{
System.Net.Http.HttpClient client =
new System.Net.Http.HttpClient(Server);
string json = string.Format(
"{{ \"Username\": \"{0}\", \"Password\": \"{1}\" }}",
"wrong", "wrong");
HttpRequestMessage request =
CreateRequest(@"api/auth/token", json, HttpMethod.Get);
Action action = () => client.SendAsync(request);
using (var response = client.SendAsync(request).Result)
{
response.StatusCode.Should()
.Be(HttpStatusCode.Unauthorized);
}
}
Я в основном получаю ошибку 404. Когда я отлаживаю его, он идет в мой DelegatingHandler
, но это не идет к моему контроллеру.
У меня такое ощущение, что я здесь упускаю суть, и, возможно, даже невозможно сделать то, что я пытаюсь сделать, но если у кого-то есть какие-либо предложения относительно того, как это сделать, или другого способа достижения одно и то же, у меня все уши.
Обновление Я думаю, что это потому, что поведение по умолчанию MockingKernel
является предоставление макета, если не указано иное, поэтому он возвращает макет IHttpControllerSelector
, Я установил пару стандартных:
kernel.Bind<IHttpControllerSelector>()
.To<DefaultHttpControllerSelector>();
kernel.Bind<IContentNegotiator>()
.To<DefaultContentNegotiator>();
Это все еще не работает, я думаю, потому что нет никаких указанных форматеров. Я попробую это завтра и посмотрю, получит ли это меня там.
1 ответ
Хорошо, я думаю, что я был прав, когда сказал, что я здесь упускаю принципиальную точку, но я отвечу на это, если это поможет кому-то еще избежать той же ошибки!
Нинъект MockingKernel
Я думаю, что в первую очередь это касается автомоделирования, поэтому, когда у вас много интерфейсов, вас не волнует, как они настроены в вашем тесте, вы можете игнорировать их в ваших тестах, и они будут автоматически созданы для вас.
В случае с Web API это, безусловно, не тот случай, так как вы не хотите, чтобы класс селектора контроллера был автоматически смоделирован, иначе вы не будете в конечном итоге вызывать ваши контроллеры.
Итак, решение, которое я придумала, это придерживаться стандартного Ninject. Kernel
, а затем привязать ваш интерфейс к постоянному объекту Mock:
kernel.Bind<IUserManager>().ToConstant(CreateUserManager());
private IUserManager CreateUserManager()
{
Mock<IUserManager> userManager = new Mock<IUserManager>();
// Set up the methods you want mocked
return userManager.Object;
}
Делая это, я смог успешно написать тесты, которые используют HttpClient для вызова HttpServer в памяти, который успешно вызывает мой DelegatingHandler
а затем в конечном итоге на моих контроллеров.