Использование 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 а затем в конечном итоге на моих контроллеров.

Другие вопросы по тегам